home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectShow / BaseClasses / winutil.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  92.3 KB  |  2,695 lines

  1. //------------------------------------------------------------------------------
  2. // File: WinUtil.cpp
  3. //
  4. // Desc: DirectShow base classes - implements generic window handler class.
  5. //
  6. // Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
  7. //------------------------------------------------------------------------------
  8.  
  9.  
  10. #include <streams.h>
  11. #include <limits.h>
  12. #include <dvdmedia.h>
  13.  
  14. static UINT MsgDestroy;
  15.  
  16. // Constructor
  17.  
  18. CBaseWindow::CBaseWindow(BOOL bDoGetDC, bool bDoPostToDestroy) :
  19.     m_hInstance(g_hInst),
  20.     m_hwnd(NULL),
  21.     m_hdc(NULL),
  22.     m_bActivated(FALSE),
  23.     m_pClassName(NULL),
  24.     m_ClassStyles(0),
  25.     m_WindowStyles(0),
  26.     m_WindowStylesEx(0),
  27.     m_ShowStageMessage(0),
  28.     m_ShowStageTop(0),
  29.     m_MemoryDC(NULL),
  30.     m_hPalette(NULL),
  31.     m_bBackground(FALSE),
  32. #ifdef DEBUG
  33.     m_bRealizing(FALSE),
  34. #endif
  35.     m_bNoRealize(FALSE),
  36.     m_bDoPostToDestroy(bDoPostToDestroy)
  37. {
  38.     m_bDoGetDC = bDoGetDC;
  39. }
  40.  
  41.  
  42. // Prepare a window by spinning off a worker thread to do the creation and
  43. // also poll the message input queue. We leave this to be called by derived
  44. // classes because they might want to override methods like MessageLoop and
  45. // InitialiseWindow, if we do this during construction they'll ALWAYS call
  46. // this base class methods. We make the worker thread create the window so
  47. // it owns it rather than the filter graph thread which is constructing us
  48.  
  49. HRESULT CBaseWindow::PrepareWindow()
  50. {
  51.     if (m_hwnd) return NOERROR;
  52.     ASSERT(m_hwnd == NULL);
  53.     ASSERT(m_hdc == NULL);
  54.  
  55.     // Get the derived object's window and class styles
  56.  
  57.     m_pClassName = GetClassWindowStyles(&m_ClassStyles,
  58.                                         &m_WindowStyles,
  59.                                         &m_WindowStylesEx);
  60.     if (m_pClassName == NULL) {
  61.         return E_FAIL;
  62.     }
  63.  
  64.     // Register our special private messages
  65.     m_ShowStageMessage = RegisterWindowMessage(SHOWSTAGE);
  66.  
  67.     // RegisterWindowMessage() returns 0 if an error occurs.
  68.     if (0 == m_ShowStageMessage) {
  69.         return AmGetLastErrorToHResult();
  70.     }
  71.  
  72.     m_ShowStageTop = RegisterWindowMessage(SHOWSTAGETOP);
  73.     if (0 == m_ShowStageTop) {
  74.         return AmGetLastErrorToHResult();
  75.     }
  76.  
  77.     m_RealizePalette = RegisterWindowMessage(REALIZEPALETTE);
  78.     if (0 == m_RealizePalette) {
  79.         return AmGetLastErrorToHResult();
  80.     }
  81.  
  82.     MsgDestroy = RegisterWindowMessage(TEXT("AM_DESTROY"));
  83.     if (0 == MsgDestroy) {
  84.         return AmGetLastErrorToHResult();
  85.     }
  86.  
  87.     return DoCreateWindow();
  88. }
  89.  
  90.  
  91. // Destructor just a placeholder so that we know it becomes virtual
  92. // Derived classes MUST call DoneWithWindow in their destructors so
  93. // that no messages arrive after the derived class constructor ends
  94.  
  95. #ifdef DEBUG
  96. CBaseWindow::~CBaseWindow()
  97. {
  98.     ASSERT(m_hwnd == NULL);
  99.     ASSERT(m_hdc == NULL);
  100. }
  101. #endif
  102.  
  103.  
  104. // We use the sync worker event to have the window destroyed. All we do is
  105. // signal the event and wait on the window thread handle. Trying to send it
  106. // messages causes too many problems, furthermore to be on the safe side we
  107. // just wait on the thread handle while it returns WAIT_TIMEOUT or there is
  108. // a sent message to process on this thread. If the constructor failed to
  109. // create the thread in the first place then the loop will get terminated
  110.  
  111. HRESULT CBaseWindow::DoneWithWindow()
  112. {
  113.     //
  114.     // Before doing anything, check that someone has not already killed the
  115.     // Video Renderer window.  If it has been killed we need to tidy up
  116.     // a DC that the window was using.  If we don't do this check
  117.     // the following GetWindowThreadProcessId test fails, but the SendMessage
  118.     // goes nowhere and we leak the DC.
  119.     //
  120.     if (!IsWindow(m_hwnd)) {
  121.  
  122.         //
  123.         // This is not a leak, the window manager automatically free's
  124.         // hdc's that were got via GetDC, which is the case here.
  125.         // We set it to NULL so that we don't get any asserts later.
  126.         //
  127.         m_hdc = NULL;
  128.  
  129.         //
  130.         // We need to free this DC though because USER32 does not know
  131.         // anything about it.
  132.         //
  133.         if (m_MemoryDC)
  134.         {
  135.             EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
  136.             m_MemoryDC = NULL;
  137.         }
  138.  
  139.         // Reset the window variables
  140.         m_hwnd = NULL;
  141.         return NOERROR;
  142.     }
  143.  
  144.  
  145.     if (GetWindowThreadProcessId(m_hwnd, NULL) != GetCurrentThreadId()) {
  146.  
  147.         if (m_bDoPostToDestroy) {
  148.  
  149.             CAMEvent m_evDone;
  150.  
  151.             //  We must post a message to destroy the window
  152.             //  That way we can't be in the middle of processing a
  153.             //  message posted to our window when we do go away
  154.             //  Sending a message gives less synchronization.
  155.             PostMessage(m_hwnd, MsgDestroy, (WPARAM)(HANDLE)m_evDone, 0);
  156.             WaitDispatchingMessages(m_evDone, INFINITE);
  157.         } else {
  158.             SendMessage(m_hwnd, MsgDestroy, 0, 0);
  159.         }
  160.         return NOERROR;
  161.     }
  162.     const HWND hwnd = m_hwnd;
  163.     if (hwnd == NULL) {
  164.         return NOERROR;
  165.     }
  166.  
  167.     InactivateWindow();
  168.     NOTE("Inactivated");
  169.  
  170.     // Reset the window styles before destruction
  171.  
  172.     SetWindowLong(hwnd,GWL_STYLE,m_WindowStyles);
  173.     ASSERT(GetParent(hwnd) == NULL);
  174.     NOTE1("Reset window styles %d",m_WindowStyles);
  175.  
  176.     //  UnintialiseWindow sets m_hwnd to NULL so save a copy
  177.     UninitialiseWindow();
  178.     DbgLog((LOG_TRACE, 2, TEXT("Destroying 0x%8.8X"), hwnd));
  179.     if (!DestroyWindow(hwnd)) {
  180.         DbgLog((LOG_TRACE, 0, TEXT("DestroyWindow %8.8X failed code %d"),
  181.                 hwnd, GetLastError()));
  182.         DbgBreak("");
  183.     }
  184.  
  185.     // Reset our state so we can be prepared again
  186.  
  187.     m_pClassName = NULL;
  188.     m_ClassStyles = 0;
  189.     m_WindowStyles = 0;
  190.     m_WindowStylesEx = 0;
  191.     m_ShowStageMessage = 0;
  192.     m_ShowStageTop = 0;
  193.  
  194.     return NOERROR;
  195. }
  196.  
  197.  
  198. // Called at the end to put the window in an inactive state. The pending list
  199. // will always have been cleared by this time so event if the worker thread
  200. // gets has been signaled and gets in to render something it will find both
  201. // the state has been changed and that there are no available sample images
  202. // Since we wait on the window thread to complete we don't lock the object
  203.  
  204. HRESULT CBaseWindow::InactivateWindow()
  205. {
  206.     // Has the window been activated
  207.     if (m_bActivated == FALSE) {
  208.         return S_FALSE;
  209.     }
  210.  
  211.     m_bActivated = FALSE;
  212.     ShowWindow(m_hwnd,SW_HIDE);
  213.     return NOERROR;
  214. }
  215.  
  216.  
  217. HRESULT CBaseWindow::CompleteConnect()
  218. {
  219.     m_bActivated = FALSE;
  220.     return NOERROR;
  221. }
  222.  
  223. // This displays a normal window. We ask the base window class for default
  224. // sizes which unless overriden will return DEFWIDTH and DEFHEIGHT. We go
  225. // through a couple of extra hoops to get the client area the right size
  226. // as the object specifies which accounts for the AdjustWindowRectEx calls
  227. // We also DWORD align the left and top coordinates of the window here to
  228. // maximise the chance of being able to use DCI/DirectDraw primary surface
  229.  
  230. HRESULT CBaseWindow::ActivateWindow()
  231. {
  232.     // Has the window been sized and positioned already
  233.  
  234.     if (m_bActivated == TRUE || GetParent(m_hwnd) != NULL) {
  235.  
  236.         SetWindowPos(m_hwnd,            // Our window handle
  237.                      HWND_TOP,          // Put it at the top
  238.                      0, 0, 0, 0,        // Leave in current position
  239.                      SWP_NOMOVE |       // Don't change it's place
  240.                      SWP_NOSIZE);       // Change Z-order only
  241.  
  242.         m_bActivated = TRUE;
  243.         return S_FALSE;
  244.     }
  245.  
  246.     // Calculate the desired client rectangle
  247.  
  248.     RECT WindowRect, ClientRect = GetDefaultRect();
  249.     GetWindowRect(m_hwnd,&WindowRect);
  250.     AdjustWindowRectEx(&ClientRect,GetWindowLong(m_hwnd,GWL_STYLE),
  251.                        FALSE,GetWindowLong(m_hwnd,GWL_EXSTYLE));
  252.  
  253.     // Align left and top edges on DWORD boundaries
  254.  
  255.     UINT WindowFlags = (SWP_NOACTIVATE | SWP_FRAMECHANGED);
  256.     WindowRect.left -= (WindowRect.left & 3);
  257.     WindowRect.top -= (WindowRect.top & 3);
  258.  
  259.     SetWindowPos(m_hwnd,                // Window handle
  260.                  HWND_TOP,              // Put it at the top
  261.                  WindowRect.left,       // Align left edge
  262.                  WindowRect.top,        // And also top place
  263.                  WIDTH(&ClientRect),    // Horizontal size
  264.                  HEIGHT(&ClientRect),   // Vertical size
  265.                  WindowFlags);          // Don't show window
  266.  
  267.     m_bActivated = TRUE;
  268.     return NOERROR;
  269. }
  270.  
  271.  
  272. // This can be used to DWORD align the window for maximum performance
  273.  
  274. HRESULT CBaseWindow::PerformanceAlignWindow()
  275. {
  276.     RECT ClientRect,WindowRect;
  277.     GetWindowRect(m_hwnd,&WindowRect);
  278.     ASSERT(m_bActivated == TRUE);
  279.  
  280.     // Don't do this if we're owned
  281.  
  282.     if (GetParent(m_hwnd)) {
  283.         return NOERROR;
  284.     }
  285.  
  286.     // Align left and top edges on DWORD boundaries
  287.  
  288.     GetClientRect(m_hwnd, &ClientRect);
  289.     MapWindowPoints(m_hwnd, HWND_DESKTOP, (LPPOINT) &ClientRect, 2);
  290.     WindowRect.left -= (ClientRect.left & 3);
  291.     WindowRect.top  -= (ClientRect.top  & 3);
  292.     UINT WindowFlags = (SWP_NOACTIVATE | SWP_NOSIZE);
  293.  
  294.     SetWindowPos(m_hwnd,                // Window handle
  295.                  HWND_TOP,              // Put it at the top
  296.                  WindowRect.left,       // Align left edge
  297.                  WindowRect.top,        // And also top place
  298.                  (int) 0,(int) 0,       // Ignore these sizes
  299.                  WindowFlags);          // Don't show window
  300.  
  301.     return NOERROR;
  302. }
  303.  
  304.  
  305. // Install a palette into the base window - we may be called by a different
  306. // thread to the one that owns the window. We have to be careful how we do
  307. // the palette realisation as we could be a different thread to the window
  308. // which would cause an inter thread send message. Therefore we realise the
  309. // palette by sending it a special message but without the window locked
  310.  
  311. HRESULT CBaseWindow::SetPalette(HPALETTE hPalette)
  312. {
  313.     // We must own the window lock during the change
  314.     {
  315.         CAutoLock cWindowLock(&m_WindowLock);
  316.         CAutoLock cPaletteLock(&m_PaletteLock);
  317.         ASSERT(hPalette);
  318.         m_hPalette = hPalette;
  319.     }
  320.     return SetPalette();
  321. }
  322.  
  323.  
  324. HRESULT CBaseWindow::SetPalette()
  325. {
  326.     if (!m_bNoRealize) {
  327.         SendMessage(m_hwnd, m_RealizePalette, 0, 0);
  328.         return S_OK;
  329.     } else {
  330.         // Just select the palette
  331.         ASSERT(m_hdc);
  332.         ASSERT(m_MemoryDC);
  333.  
  334.         CAutoLock cPaletteLock(&m_PaletteLock);
  335.         SelectPalette(m_hdc,m_hPalette,m_bBackground);
  336.         SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
  337.  
  338.         return S_OK;
  339.     }
  340. }
  341.  
  342.  
  343. void CBaseWindow::UnsetPalette()
  344. {
  345.     CAutoLock cWindowLock(&m_WindowLock);
  346.     CAutoLock cPaletteLock(&m_PaletteLock);
  347.  
  348.     // Get a standard VGA colour palette
  349.  
  350.     HPALETTE hPalette = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
  351.     ASSERT(hPalette);
  352.  
  353.     SelectPalette(GetWindowHDC(), hPalette, TRUE);
  354.     SelectPalette(GetMemoryHDC(), hPalette, TRUE);
  355.  
  356.     m_hPalette = NULL;
  357. }
  358.  
  359.  
  360. void CBaseWindow::LockPaletteLock()
  361. {
  362.     m_PaletteLock.Lock();
  363. }
  364.  
  365.  
  366. void CBaseWindow::UnlockPaletteLock()
  367. {
  368.     m_PaletteLock.Unlock();
  369. }
  370.  
  371.  
  372. // Realise our palettes in the window and device contexts
  373.  
  374. HRESULT CBaseWindow::DoRealisePalette(BOOL bForceBackground)
  375. {
  376.     {
  377.         CAutoLock cPaletteLock(&m_PaletteLock);
  378.  
  379.         if (m_hPalette == NULL) {
  380.             return NOERROR;
  381.         }
  382.  
  383.         // Realize the palette on the window thread
  384.         ASSERT(m_hdc);
  385.         ASSERT(m_MemoryDC);
  386.  
  387.         SelectPalette(m_hdc,m_hPalette,m_bBackground || bForceBackground);
  388.         SelectPalette(m_MemoryDC,m_hPalette,m_bBackground);
  389.     }
  390.  
  391.     //  If we grab a critical section here we can deadlock
  392.     //  with the window thread because one of the side effects
  393.     //  of RealizePalette is to send a WM_PALETTECHANGED message
  394.     //  to every window in the system.  In our handling
  395.     //  of WM_PALETTECHANGED we used to grab this CS too.
  396.     //  The really bad case is when our renderer calls DoRealisePalette()
  397.     //  while we're in the middle of processing a palette change
  398.     //  for another window.
  399.     //  So don't hold the critical section while actually realising
  400.     //  the palette.  In any case USER is meant to manage palette
  401.     //  handling - we shouldn't have to serialize everything as well
  402.     ASSERT(CritCheckOut(&m_WindowLock));
  403.     ASSERT(CritCheckOut(&m_PaletteLock));
  404.  
  405.     EXECUTE_ASSERT(RealizePalette(m_hdc) != GDI_ERROR);
  406.     EXECUTE_ASSERT(RealizePalette(m_MemoryDC) != GDI_ERROR);
  407.  
  408.     return (GdiFlush() == FALSE ? S_FALSE : S_OK);
  409. }
  410.  
  411.  
  412. // This is the global window procedure
  413.  
  414. LRESULT CALLBACK WndProc(HWND hwnd,         // Window handle
  415.                          UINT uMsg,         // Message ID
  416.                          WPARAM wParam,     // First parameter
  417.                          LPARAM lParam)     // Other parameter
  418. {
  419.  
  420.     // Get the window long that holds our window object pointer
  421.     // If it is NULL then we are initialising the window in which
  422.     // case the object pointer has been passed in the window creation
  423.     // structure.  IF we get any messages before WM_NCCREATE we will
  424.     // pass them to DefWindowProc.
  425.  
  426.     CBaseWindow *pBaseWindow = (CBaseWindow *)GetWindowLongPtr(hwnd,0);
  427.     if (pBaseWindow == NULL) {
  428.  
  429.         // Get the structure pointer from the create struct.
  430.         // We can only do this for WM_NCCREATE which should be one of
  431.         // the first messages we receive.  Anything before this will
  432.         // have to be passed to DefWindowProc (i.e. WM_GETMINMAXINFO)
  433.  
  434.         // If the message is WM_NCCREATE we set our pBaseWindow pointer
  435.         // and will then place it in the window structure
  436.  
  437.         // turn off WS_EX_LAYOUTRTL style for quartz windows
  438.         if (uMsg == WM_NCCREATE) {
  439.             SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) & ~0x400000);
  440.         }
  441.  
  442.         if ((uMsg != WM_NCCREATE)
  443.             || (NULL == (pBaseWindow = *(CBaseWindow**) ((LPCREATESTRUCT)lParam)->lpCreateParams)))
  444.         {
  445.             return(DefWindowProc(hwnd, uMsg, wParam, lParam));
  446.         }
  447.  
  448.         // Set the window LONG to be the object who created us
  449. #ifdef DEBUG
  450.         SetLastError(0);  // because of the way SetWindowLong works
  451. #endif
  452.         LONG_PTR rc = SetWindowLongPtr(hwnd, (DWORD) 0, (LONG_PTR) pBaseWindow);
  453. #ifdef DEBUG
  454.         if (0 == rc) {
  455.             // SetWindowLong MIGHT have failed.  (Read the docs which admit
  456.             // that it is awkward to work out if you have had an error.)
  457.             LONG lasterror = GetLastError();
  458.             ASSERT(0 == lasterror);
  459.             // If this is not the case we have not set the pBaseWindow pointer
  460.             // into the window structure and we will blow up.
  461.         }
  462. #endif
  463.  
  464.     }
  465.     // See if this is the packet of death
  466.     if (uMsg == MsgDestroy && uMsg != 0) {
  467.         pBaseWindow->DoneWithWindow();
  468.         if (pBaseWindow->m_bDoPostToDestroy) {
  469.             EXECUTE_ASSERT(SetEvent((HANDLE)wParam));
  470.         }
  471.         return 0;
  472.     }
  473.     return pBaseWindow->OnReceiveMessage(hwnd,uMsg,wParam,lParam);
  474. }
  475.  
  476.  
  477. // When the window size changes we adjust our member variables that
  478. // contain the dimensions of the client rectangle for our window so
  479. // that we come to render an image we will know whether to stretch
  480.  
  481. BOOL CBaseWindow::OnSize(LONG Width, LONG Height)
  482. {
  483.     m_Width = Width;
  484.     m_Height = Height;
  485.     return TRUE;
  486. }
  487.  
  488.  
  489. // This function handles the WM_CLOSE message
  490.  
  491. BOOL CBaseWindow::OnClose()
  492. {
  493.     ShowWindow(m_hwnd,SW_HIDE);
  494.     return TRUE;
  495. }
  496.  
  497.  
  498. // This is called by the worker window thread when it receives a terminate
  499. // message from the window object destructor to delete all the resources we
  500. // allocated during initialisation. By the time the worker thread exits all
  501. // processing will have been completed as the source filter disconnection
  502. // flushes the image pending sample, therefore the GdiFlush should succeed
  503.  
  504. HRESULT CBaseWindow::UninitialiseWindow()
  505. {
  506.     // Have we already cleaned up
  507.  
  508.     if (m_hwnd == NULL) {
  509.         ASSERT(m_hdc == NULL);
  510.         ASSERT(m_MemoryDC == NULL);
  511.         return NOERROR;
  512.     }
  513.  
  514.     // Release the window resources
  515.  
  516.     EXECUTE_ASSERT(GdiFlush());
  517.  
  518.     if (m_hdc)
  519.     {
  520.         EXECUTE_ASSERT(ReleaseDC(m_hwnd,m_hdc));
  521.         m_hdc = NULL;
  522.     }
  523.  
  524.     if (m_MemoryDC)
  525.     {
  526.         EXECUTE_ASSERT(DeleteDC(m_MemoryDC));
  527.         m_MemoryDC = NULL;
  528.     }
  529.  
  530.     // Reset the window variables
  531.     m_hwnd = NULL;
  532.  
  533.     return NOERROR;
  534. }
  535.  
  536.  
  537. // This is called by the worker window thread after it has created the main
  538. // window and it wants to initialise the rest of the owner objects window
  539. // variables such as the device contexts. We execute this function with the
  540. // critical section still locked. Nothing in this function must generate any
  541. // SendMessage calls to the window because this is executing on the window
  542. // thread so the message will never be processed and we will deadlock
  543.  
  544. #pragma warning(disable:4706)  // C4706: assignment within conditional expression
  545.  
  546. HRESULT CBaseWindow::InitialiseWindow(HWND hwnd)
  547. {
  548.     // Initialise the window variables
  549.  
  550.     ASSERT(IsWindow(hwnd));
  551.     m_hwnd = hwnd;
  552.  
  553.     if (m_bDoGetDC)
  554.     {
  555.         EXECUTE_ASSERT(m_hdc = GetDC(hwnd));
  556.         EXECUTE_ASSERT(m_MemoryDC = CreateCompatibleDC(m_hdc));
  557.  
  558.         EXECUTE_ASSERT(SetStretchBltMode(m_hdc,COLORONCOLOR));
  559.         EXECUTE_ASSERT(SetStretchBltMode(m_MemoryDC,COLORONCOLOR));
  560.     }
  561.  
  562.     return NOERROR;
  563. }
  564.  
  565. #pragma warning(default:4706)  // C4706: assignment within conditional expression
  566.  
  567.  
  568. HRESULT CBaseWindow::DoCreateWindow()
  569. {
  570.     WNDCLASS wndclass;                  // Used to register classes
  571.     BOOL bRegistered;                   // Is this class registered
  572.     HWND hwnd;                          // Handle to our window
  573.  
  574.     bRegistered = GetClassInfo(m_hInstance,   // Module instance
  575.                                m_pClassName,  // Window class
  576.                                &wndclass);                 // Info structure
  577.  
  578.     // if the window is to be used for drawing puposes and we are getting a DC
  579.     // for the entire lifetime of the window then changes the class style to do
  580.     // say so. If we don't set this flag then the DC comes from the cache and is
  581.     // really bad.
  582.     if (m_bDoGetDC)
  583.     {
  584.         m_ClassStyles |= CS_OWNDC;
  585.     }
  586.  
  587.     if (bRegistered == FALSE) {
  588.  
  589.         // Register the renderer window class
  590.  
  591.         wndclass.lpszClassName = m_pClassName;
  592.         wndclass.style         = m_ClassStyles;
  593.         wndclass.lpfnWndProc   = WndProc;
  594.         wndclass.cbClsExtra    = 0;
  595.         wndclass.cbWndExtra    = sizeof(CBaseWindow *);
  596.         wndclass.hInstance     = m_hInstance;
  597.         wndclass.hIcon         = NULL;
  598.         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW);
  599.         wndclass.hbrBackground = (HBRUSH) NULL;
  600.         wndclass.lpszMenuName  = NULL;
  601.  
  602.         RegisterClass(&wndclass);
  603.     }
  604.  
  605.     // Create the frame window.  Pass the pBaseWindow information in the
  606.     // CreateStruct which allows our message handling loop to get hold of
  607.     // the pBaseWindow pointer.
  608.  
  609.     CBaseWindow *pBaseWindow = this;                      // The owner window object
  610.     hwnd = CreateWindowEx(m_WindowStylesEx,               // Extended styles
  611.                           m_pClassName,                   // Registered name
  612.                           TEXT("ActiveMovie Window"),     // Window title
  613.                           m_WindowStyles,                 // Window styles
  614.                           CW_USEDEFAULT,                  // Start x position
  615.                           CW_USEDEFAULT,                  // Start y position
  616.                           DEFWIDTH,                       // Window width
  617.                           DEFHEIGHT,                      // Window height
  618.                           NULL,                           // Parent handle
  619.                           NULL,                           // Menu handle
  620.                           m_hInstance,                    // Instance handle
  621.                           &pBaseWindow);                  // Creation data
  622.  
  623.     // If we failed signal an error to the object constructor (based on the
  624.     // last Win32 error on this thread) then signal the constructor thread
  625.     // to continue, release the mutex to let others have a go and exit
  626.  
  627.     if (hwnd == NULL) {
  628.         DWORD Error = GetLastError();
  629.         return AmHresultFromWin32(Error);
  630.     }
  631.  
  632.     // Check the window LONG is the object who created us
  633.     ASSERT(GetWindowLongPtr(hwnd, 0) == (LONG_PTR)this);
  634.  
  635.     // Initialise the window and then signal the constructor so that it can
  636.     // continue and then finally unlock the object's critical section. The
  637.     // window class is left registered even after we terminate the thread
  638.     // as we don't know when the last window has been closed. So we allow
  639.     // the operating system to free the class resources as appropriate
  640.  
  641.     InitialiseWindow(hwnd);
  642.  
  643.     DbgLog((LOG_TRACE, 2, TEXT("Created window class (%s) HWND(%8.8X)"),
  644.             m_pClassName, hwnd));
  645.  
  646.     return S_OK;
  647. }
  648.  
  649.  
  650. // The base class provides some default handling and calls DefWindowProc
  651.  
  652. LRESULT CBaseWindow::OnReceiveMessage(HWND hwnd,         // Window handle
  653.                                       UINT uMsg,         // Message ID
  654.                                       WPARAM wParam,     // First parameter
  655.                                       LPARAM lParam)     // Other parameter
  656. {
  657.     ASSERT(IsWindow(hwnd));
  658.  
  659.     if (PossiblyEatMessage(uMsg, wParam, lParam))
  660.         return 0;
  661.  
  662.     // This is sent by the IVideoWindow SetWindowForeground method. If the
  663.     // window is invisible we will show it and make it topmost without the
  664.     // foreground focus. If the window is visible it will also be made the
  665.     // topmost window without the foreground focus. If wParam is TRUE then
  666.     // for both cases the window will be forced into the foreground focus
  667.  
  668.     if (uMsg == m_ShowStageMessage) {
  669.  
  670.         BOOL bVisible = IsWindowVisible(hwnd);
  671.         SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
  672.                      SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW |
  673.                      (bVisible ? SWP_NOACTIVATE : 0));
  674.  
  675.         // Should we bring the window to the foreground
  676.         if (wParam == TRUE) {
  677.             SetForegroundWindow(hwnd);
  678.         }
  679.         return (LRESULT) 1;
  680.     }
  681.  
  682.     // When we go fullscreen we have to add the WS_EX_TOPMOST style to the
  683.     // video window so that it comes out above any task bar (this is more
  684.     // relevant to WindowsNT than Windows95). However the SetWindowPos call
  685.     // must be on the same thread as that which created the window. The
  686.     // wParam parameter can be TRUE or FALSE to set and reset the topmost
  687.  
  688.     if (uMsg == m_ShowStageTop) {
  689.         HWND HwndTop = (wParam == TRUE ? HWND_TOPMOST : HWND_NOTOPMOST);
  690.         BOOL bVisible = IsWindowVisible(hwnd);
  691.         SetWindowPos(hwnd, HwndTop, 0, 0, 0, 0,
  692.                      SWP_NOMOVE | SWP_NOSIZE |
  693.                      (wParam == TRUE ? SWP_SHOWWINDOW : 0) |
  694.                      (bVisible ? SWP_NOACTIVATE : 0));
  695.         return (LRESULT) 1;
  696.     }
  697.  
  698.     // New palette stuff
  699.     if (uMsg == m_RealizePalette) {
  700.         ASSERT(m_hwnd == hwnd);
  701.         return OnPaletteChange(m_hwnd,WM_QUERYNEWPALETTE);
  702.     }
  703.  
  704.     switch (uMsg) {
  705.  
  706.         // Repaint the window if the system colours change
  707.  
  708.     case WM_SYSCOLORCHANGE:
  709.  
  710.         InvalidateRect(hwnd,NULL,FALSE);
  711.         return (LRESULT) 1;
  712.  
  713.     // Somebody has changed the palette
  714.     case WM_PALETTECHANGED:
  715.  
  716.         OnPaletteChange((HWND)wParam,uMsg);
  717.         return (LRESULT) 0;
  718.  
  719.         // We are about to receive the keyboard focus so we ask GDI to realise
  720.         // our logical palette again and hopefully it will be fully installed
  721.         // without any mapping having to be done during any picture rendering
  722.  
  723.     case WM_QUERYNEWPALETTE:
  724.         ASSERT(m_hwnd == hwnd);
  725.         return OnPaletteChange(m_hwnd,uMsg);
  726.  
  727.     // do NOT fwd WM_MOVE. the parameters are the location of the parent
  728.     // window, NOT what the renderer should be looking at.  But we need
  729.     // to make sure the overlay is moved with the parent window, so we
  730.     // do this.
  731.     case WM_MOVE:
  732.         if (IsWindowVisible(m_hwnd)) {
  733.             PostMessage(m_hwnd,WM_PAINT,0,0);
  734.         }
  735.         break;
  736.  
  737.     // Store the width and height as useful base class members
  738.  
  739.     case WM_SIZE:
  740.  
  741.         OnSize(LOWORD(lParam), HIWORD(lParam));
  742.         return (LRESULT) 0;
  743.  
  744.     // Intercept the WM_CLOSE messages to hide the window
  745.  
  746.     case WM_CLOSE:
  747.  
  748.         OnClose();
  749.         return (LRESULT) 0;
  750.     }
  751.     return DefWindowProc(hwnd,uMsg,wParam,lParam);
  752. }
  753.  
  754.  
  755. // This handles the Windows palette change messages - if we do realise our
  756. // palette then we return TRUE otherwise we return FALSE. If our window is
  757. // foreground application then we should get first choice of colours in the
  758. // system palette entries. We get best performance when our logical palette
  759. // includes the standard VGA colours (at the beginning and end) otherwise
  760. // GDI may have to map from our palette to the device palette while drawing
  761.  
  762. LRESULT CBaseWindow::OnPaletteChange(HWND hwnd,UINT Message)
  763. {
  764.     // First check we are not changing the palette during closedown
  765.  
  766.     if (m_hwnd == NULL || hwnd == NULL) {
  767.         return (LRESULT) 0;
  768.     }
  769.     ASSERT(!m_bRealizing);
  770.  
  771.     // Should we realise our palette again
  772.  
  773.     if ((Message == WM_QUERYNEWPALETTE || hwnd != m_hwnd)) {
  774.         //  It seems that even if we're invisible that we can get asked
  775.         //  to realize our palette and this can cause really ugly side-effects
  776.         //  Seems like there's another bug but this masks it a least for the
  777.         //  shutting down case.
  778.         if (!IsWindowVisible(m_hwnd)) {
  779.             DbgLog((LOG_TRACE, 1, TEXT("Realizing when invisible!")));
  780.             return (LRESULT) 0;
  781.         }
  782.  
  783.         // Avoid recursion with multiple graphs in the same app
  784. #ifdef DEBUG
  785.         m_bRealizing = TRUE;
  786. #endif
  787.         DoRealisePalette(Message != WM_QUERYNEWPALETTE);
  788. #ifdef DEBUG
  789.         m_bRealizing = FALSE;
  790. #endif
  791.  
  792.         // Should we redraw the window with the new palette
  793.         if (Message == WM_PALETTECHANGED) {
  794.             InvalidateRect(m_hwnd,NULL,FALSE);
  795.         }
  796.     }
  797.  
  798.     return (LRESULT) 1;
  799. }
  800.  
  801.  
  802. // Determine if the window exists.
  803.  
  804. bool CBaseWindow::WindowExists()
  805. {
  806.     return !!IsWindow(m_hwnd);
  807. }
  808.  
  809.  
  810. // Return the default window rectangle
  811.  
  812. RECT CBaseWindow::GetDefaultRect()
  813. {
  814.     RECT DefaultRect = {0,0,DEFWIDTH,DEFHEIGHT};
  815.     ASSERT(m_hwnd);
  816.     // ASSERT(m_hdc);
  817.     return DefaultRect;
  818. }
  819.  
  820.  
  821. // Return the current window width
  822.  
  823. LONG CBaseWindow::GetWindowWidth()
  824. {
  825.     ASSERT(m_hwnd);
  826.     // ASSERT(m_hdc);
  827.     return m_Width;
  828. }
  829.  
  830.  
  831. // Return the current window height
  832.  
  833. LONG CBaseWindow::GetWindowHeight()
  834. {
  835.     ASSERT(m_hwnd);
  836.     // ASSERT(m_hdc);
  837.     return m_Height;
  838. }
  839.  
  840.  
  841. // Return the window handle
  842.  
  843. HWND CBaseWindow::GetWindowHWND()
  844. {
  845.     ASSERT(m_hwnd);
  846.     // ASSERT(m_hdc);
  847.     return m_hwnd;
  848. }
  849.  
  850.  
  851. // Return the window drawing device context
  852.  
  853. HDC CBaseWindow::GetWindowHDC()
  854. {
  855.     ASSERT(m_hwnd);
  856.     ASSERT(m_hdc);
  857.     return m_hdc;
  858. }
  859.  
  860.  
  861. // Return the offscreen window drawing device context
  862.  
  863. HDC CBaseWindow::GetMemoryHDC()
  864. {
  865.     ASSERT(m_hwnd);
  866.     ASSERT(m_MemoryDC);
  867.     return m_MemoryDC;
  868. }
  869.  
  870.  
  871. #ifdef DEBUG
  872. HPALETTE CBaseWindow::GetPalette()
  873. {
  874.     // The palette lock should always be held when accessing
  875.     // m_hPalette.
  876.     ASSERT(CritCheckIn(&m_PaletteLock));
  877.     return m_hPalette;
  878. }
  879. #endif // DEBUG
  880.  
  881.  
  882. // This is available to clients who want to change the window visiblity. It's
  883. // little more than an indirection to the Win32 ShowWindow although these is
  884. // some benefit in going through here as this function may change sometime
  885.  
  886. HRESULT CBaseWindow::DoShowWindow(LONG ShowCmd)
  887. {
  888.     ShowWindow(m_hwnd,ShowCmd);
  889.     return NOERROR;
  890. }
  891.  
  892.  
  893. // Generate a WM_PAINT message for the video window
  894.  
  895. void CBaseWindow::PaintWindow(BOOL bErase)
  896. {
  897.     InvalidateRect(m_hwnd,NULL,bErase);
  898. }
  899.  
  900.  
  901. // Allow an application to have us set the video window in the foreground. We
  902. // have this because it is difficult for one thread to do do this to a window
  903. // owned by another thread. Rather than expose the message we use to execute
  904. // the inter thread send message we provide the interface function. All we do
  905. // is to SendMessage to the video window renderer thread with a WM_SHOWSTAGE
  906.  
  907. void CBaseWindow::DoSetWindowForeground(BOOL bFocus)
  908. {
  909.     SendMessage(m_hwnd,m_ShowStageMessage,(WPARAM) bFocus,(LPARAM) 0);
  910. }
  911.  
  912.  
  913. // Constructor initialises the owning object pointer. Since we are a worker
  914. // class for the main window object we have relatively few state variables to
  915. // look after. We are given device context handles to use later on as well as
  916. // the source and destination rectangles (but reset them here just in case)
  917.  
  918. CDrawImage::CDrawImage(CBaseWindow *pBaseWindow) :
  919.     m_pBaseWindow(pBaseWindow),
  920.     m_hdc(NULL),
  921.     m_MemoryDC(NULL),
  922.     m_bStretch(FALSE),
  923.     m_pMediaType(NULL),
  924.     m_bUsingImageAllocator(FALSE)
  925. {
  926.     ASSERT(pBaseWindow);
  927.     ResetPaletteVersion();
  928.     SetRectEmpty(&m_TargetRect);
  929.     SetRectEmpty(&m_SourceRect);
  930.  
  931.     m_perfidRenderTime = MSR_REGISTER(TEXT("Single Blt time"));
  932. }
  933.  
  934.  
  935. // Overlay the image time stamps on the picture. Access to this method is
  936. // serialised by the caller. We display the sample start and end times on
  937. // top of the video using TextOut on the device context we are handed. If
  938. // there isn't enough room in the window for the times we don't show them
  939.  
  940. void CDrawImage::DisplaySampleTimes(IMediaSample *pSample)
  941. {
  942. #ifdef DEBUG
  943.     //
  944.     // Only allow the "annoying" time messages if the users has turned the
  945.     // logging "way up"
  946.     //
  947.     BOOL bAccept = DbgCheckModuleLevel(LOG_TRACE, 5);
  948.     if (bAccept == FALSE) {
  949.         return;
  950.     }
  951. #endif
  952.  
  953.     TCHAR szTimes[TIMELENGTH];      // Time stamp strings
  954.     ASSERT(pSample);                // Quick sanity check
  955.     RECT ClientRect;                // Client window size
  956.     SIZE Size;                      // Size of text output
  957.  
  958.     // Get the time stamps and window size
  959.  
  960.     pSample->GetTime((REFERENCE_TIME*)&m_StartSample, (REFERENCE_TIME*)&m_EndSample);
  961.     HWND hwnd = m_pBaseWindow->GetWindowHWND();
  962.     EXECUTE_ASSERT(GetClientRect(hwnd,&ClientRect));
  963.  
  964.     // Format the sample time stamps
  965.  
  966.     wsprintf(szTimes,TEXT("%08d : %08d"),
  967.              m_StartSample.Millisecs(),
  968.              m_EndSample.Millisecs());
  969.  
  970.     ASSERT(lstrlen(szTimes) < TIMELENGTH);
  971.  
  972.     // Put the times in the middle at the bottom of the window
  973.  
  974.     GetTextExtentPoint32(m_hdc,szTimes,lstrlen(szTimes),&Size);
  975.     INT XPos = ((ClientRect.right - ClientRect.left) - Size.cx) / 2;
  976.     INT YPos = ((ClientRect.bottom - ClientRect.top) - Size.cy) * 4 / 5;
  977.  
  978.     // Check the window is big enough to have sample times displayed
  979.  
  980.     if ((XPos > 0) && (YPos > 0)) {
  981.         TextOut(m_hdc,XPos,YPos,szTimes,lstrlen(szTimes));
  982.     }
  983. }
  984.  
  985.  
  986. // This is called when the drawing code sees that the image has a down level
  987. // palette cookie. We simply call the SetDIBColorTable Windows API with the
  988. // palette that is found after the BITMAPINFOHEADER - we return no errors
  989.  
  990. void CDrawImage::UpdateColourTable(HDC hdc,BITMAPINFOHEADER *pbmi)
  991. {
  992.     ASSERT(pbmi->biClrUsed);
  993.     RGBQUAD *pColourTable = (RGBQUAD *)(pbmi+1);
  994.  
  995.     // Set the new palette in the device context
  996.  
  997.     UINT uiReturn = SetDIBColorTable(hdc,(UINT) 0,
  998.                                      pbmi->biClrUsed,
  999.                                      pColourTable);
  1000.  
  1001.     // Should always succeed but check in debug builds
  1002.     ASSERT(uiReturn == pbmi->biClrUsed);
  1003. }
  1004.  
  1005.  
  1006. // No source rectangle scaling is done by the base class
  1007.  
  1008. RECT CDrawImage::ScaleSourceRect(const RECT *pSource)
  1009. {
  1010.     ASSERT(pSource);
  1011.     return *pSource;
  1012. }
  1013.  
  1014.  
  1015. // This is called when the funky output pin uses our allocator. The samples we
  1016. // allocate are special because the memory is shared between us and GDI thus
  1017. // removing one copy when we ask for the image to be rendered. The source type
  1018. // information is in the main renderer m_mtIn field which is initialised when
  1019. // the media type is agreed in SetMediaType, the media type may be changed on
  1020. // the fly if, for example, the source filter needs to change the palette
  1021.  
  1022. void CDrawImage::FastRender(IMediaSample *pMediaSample)
  1023. {
  1024.     BITMAPINFOHEADER *pbmi;     // Image format data
  1025.     DIBDATA *pDibData;          // Stores DIB information
  1026.     BYTE *pImage;               // Pointer to image data
  1027.     HBITMAP hOldBitmap;         // Store the old bitmap
  1028.     CImageSample *pSample;      // Pointer to C++ object
  1029.  
  1030.     ASSERT(m_pMediaType);
  1031.  
  1032.     // From the untyped source format block get the VIDEOINFO and subsequently
  1033.     // the BITMAPINFOHEADER structure. We can cast the IMediaSample interface
  1034.     // to a CImageSample object so we can retrieve it's DIBSECTION details
  1035.  
  1036.     pbmi = HEADER(m_pMediaType->Format());
  1037.     pSample = (CImageSample *) pMediaSample;
  1038.     pDibData = pSample->GetDIBData();
  1039.     hOldBitmap = (HBITMAP) SelectObject(m_MemoryDC,pDibData->hBitmap);
  1040.  
  1041.     // Get a pointer to the real image data
  1042.  
  1043.     HRESULT hr = pMediaSample->GetPointer(&pImage);
  1044.     if (FAILED(hr)) {
  1045.         return;
  1046.     }
  1047.  
  1048.     // Do we need to update the colour table, we increment our palette cookie
  1049.     // each time we get a dynamic format change. The sample palette cookie is
  1050.     // stored in the DIBDATA structure so we try to keep the fields in sync
  1051.     // By the time we get to draw the images the format change will be done
  1052.     // so all we do is ask the renderer for what it's palette version is
  1053.  
  1054.     if (pDibData->PaletteVersion < GetPaletteVersion()) {
  1055.         ASSERT(pbmi->biBitCount <= iPALETTE);
  1056.         UpdateColourTable(m_MemoryDC,pbmi);
  1057.         pDibData->PaletteVersion = GetPaletteVersion();
  1058.     }
  1059.  
  1060.     // This allows derived classes to change the source rectangle that we do
  1061.     // the drawing with. For example a renderer may ask a codec to stretch
  1062.     // the video from 320x240 to 640x480, in which case the source we see in
  1063.     // here will still be 320x240, although the source we want to draw with
  1064.     // should be scaled up to 640x480. The base class implementation of this
  1065.     // method does nothing but return the same rectangle as we are passed in
  1066.  
  1067.     RECT SourceRect = ScaleSourceRect(&m_SourceRect);
  1068.  
  1069.     // Is the window the same size as the video
  1070.  
  1071.     if (m_bStretch == FALSE) {
  1072.  
  1073.         // Put the image straight into the window
  1074.  
  1075.         BitBlt(
  1076.             (HDC) m_hdc,                            // Target device HDC
  1077.             m_TargetRect.left,                      // X sink position
  1078.             m_TargetRect.top,                       // Y sink position
  1079.             m_TargetRect.right - m_TargetRect.left, // Destination width
  1080.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  1081.             m_MemoryDC,                             // Source device context
  1082.             SourceRect.left,                        // X source position
  1083.             SourceRect.top,                         // Y source position
  1084.             SRCCOPY);                               // Simple copy
  1085.  
  1086.     } else {
  1087.  
  1088.         // Stretch the image when copying to the window
  1089.  
  1090.         StretchBlt(
  1091.             (HDC) m_hdc,                            // Target device HDC
  1092.             m_TargetRect.left,                      // X sink position
  1093.             m_TargetRect.top,                       // Y sink position
  1094.             m_TargetRect.right - m_TargetRect.left, // Destination width
  1095.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  1096.             m_MemoryDC,                             // Source device HDC
  1097.             SourceRect.left,                        // X source position
  1098.             SourceRect.top,                         // Y source position
  1099.             SourceRect.right - SourceRect.left,     // Source width
  1100.             SourceRect.bottom - SourceRect.top,     // Source height
  1101.             SRCCOPY);                               // Simple copy
  1102.     }
  1103.  
  1104.     // This displays the sample times over the top of the image. This used to
  1105.     // draw the times into the offscreen device context however that actually
  1106.     // writes the text into the image data buffer which may not be writable
  1107.  
  1108.     #ifdef DEBUG
  1109.     DisplaySampleTimes(pMediaSample);
  1110.     #endif
  1111.  
  1112.     // Put the old bitmap back into the device context so we don't leak
  1113.     SelectObject(m_MemoryDC,hOldBitmap);
  1114. }
  1115.  
  1116.  
  1117. // This is called when there is a sample ready to be drawn, unfortunately the
  1118. // output pin was being rotten and didn't choose our super excellent shared
  1119. // memory DIB allocator so we have to do this slow render using boring old GDI
  1120. // SetDIBitsToDevice and StretchDIBits. The down side of using these GDI
  1121. // functions is that the image data has to be copied across from our address
  1122. // space into theirs before going to the screen (although in reality the cost
  1123. // is small because all they do is to map the buffer into their address space)
  1124.  
  1125. void CDrawImage::SlowRender(IMediaSample *pMediaSample)
  1126. {
  1127.     // Get the BITMAPINFOHEADER for the connection
  1128.  
  1129.     ASSERT(m_pMediaType);
  1130.     BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
  1131.     BYTE *pImage;
  1132.  
  1133.     // Get the image data buffer
  1134.  
  1135.     HRESULT hr = pMediaSample->GetPointer(&pImage);
  1136.     if (FAILED(hr)) {
  1137.         return;
  1138.     }
  1139.  
  1140.     // This allows derived classes to change the source rectangle that we do
  1141.     // the drawing with. For example a renderer may ask a codec to stretch
  1142.     // the video from 320x240 to 640x480, in which case the source we see in
  1143.     // here will still be 320x240, although the source we want to draw with
  1144.     // should be scaled up to 640x480. The base class implementation of this
  1145.     // method does nothing but return the same rectangle as we are passed in
  1146.  
  1147.     RECT SourceRect = ScaleSourceRect(&m_SourceRect);
  1148.  
  1149.     LONG lAdjustedSourceTop = SourceRect.top;
  1150.     // if the origin of bitmap is bottom-left, adjust soruce_rect_top
  1151.     // to be the bottom-left corner instead of the top-left.
  1152.     if (pbmi->biHeight > 0) {
  1153.        lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;
  1154.     }
  1155.     // Is the window the same size as the video
  1156.  
  1157.     if (m_bStretch == FALSE) {
  1158.  
  1159.         // Put the image straight into the window
  1160.  
  1161.         SetDIBitsToDevice(
  1162.             (HDC) m_hdc,                            // Target device HDC
  1163.             m_TargetRect.left,                      // X sink position
  1164.             m_TargetRect.top,                       // Y sink position
  1165.             m_TargetRect.right - m_TargetRect.left, // Destination width
  1166.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  1167.             SourceRect.left,                        // X source position
  1168.             lAdjustedSourceTop,                     // Adjusted Y source position
  1169.             (UINT) 0,                               // Start scan line
  1170.             pbmi->biHeight,                         // Scan lines present
  1171.             pImage,                                 // Image data
  1172.             (BITMAPINFO *) pbmi,                    // DIB header
  1173.             DIB_RGB_COLORS);                        // Type of palette
  1174.  
  1175.     } else {
  1176.  
  1177.         // Stretch the image when copying to the window
  1178.  
  1179.         StretchDIBits(
  1180.             (HDC) m_hdc,                            // Target device HDC
  1181.             m_TargetRect.left,                      // X sink position
  1182.             m_TargetRect.top,                       // Y sink position
  1183.             m_TargetRect.right - m_TargetRect.left, // Destination width
  1184.             m_TargetRect.bottom - m_TargetRect.top, // Destination height
  1185.             SourceRect.left,                        // X source position
  1186.             lAdjustedSourceTop,                     // Adjusted Y source position
  1187.             SourceRect.right - SourceRect.left,     // Source width
  1188.             SourceRect.bottom - SourceRect.top,     // Source height
  1189.             pImage,                                 // Image data
  1190.             (BITMAPINFO *) pbmi,                    // DIB header
  1191.             DIB_RGB_COLORS,                         // Type of palette
  1192.             SRCCOPY);                               // Simple image copy
  1193.     }
  1194.  
  1195.     // This shows the sample reference times over the top of the image which
  1196.     // looks a little flickery. I tried using GdiSetBatchLimit and GdiFlush to
  1197.     // control the screen updates but it doesn't quite work as expected and
  1198.     // only partially reduces the flicker. I also tried using a memory context
  1199.     // and combining the two in that before doing a final BitBlt operation to
  1200.     // the screen, unfortunately this has considerable performance penalties
  1201.     // and also means that this code is not executed when compiled retail
  1202.  
  1203.     #ifdef DEBUG
  1204.     DisplaySampleTimes(pMediaSample);
  1205.     #endif
  1206. }
  1207.  
  1208.  
  1209. // This is called with an IMediaSample interface on the image to be drawn. We
  1210. // decide on the drawing mechanism based on who's allocator we are using. We
  1211. // may be called when the window wants an image painted by WM_PAINT messages
  1212. // We can't realise the palette here because we have the renderer lock, any
  1213. // call to realise may cause an interthread send message to the window thread
  1214. // which may in turn be waiting to get the renderer lock before servicing it
  1215.  
  1216. BOOL CDrawImage::DrawImage(IMediaSample *pMediaSample)
  1217. {
  1218.     ASSERT(m_hdc);
  1219.     ASSERT(m_MemoryDC);
  1220.     NotifyStartDraw();
  1221.  
  1222.     // If the output pin used our allocator then the samples passed are in
  1223.     // fact CVideoSample objects that contain CreateDIBSection data that we
  1224.     // use to do faster image rendering, they may optionally also contain a
  1225.     // DirectDraw surface pointer in which case we do not do the drawing
  1226.  
  1227.     if (m_bUsingImageAllocator == FALSE) {
  1228.         SlowRender(pMediaSample);
  1229.         EXECUTE_ASSERT(GdiFlush());
  1230.         NotifyEndDraw();
  1231.         return TRUE;
  1232.     }
  1233.  
  1234.     // This is a DIBSECTION buffer
  1235.  
  1236.     FastRender(pMediaSample);
  1237.     EXECUTE_ASSERT(GdiFlush());
  1238.     NotifyEndDraw();
  1239.     return TRUE;
  1240. }
  1241.  
  1242.  
  1243. BOOL CDrawImage::DrawVideoImageHere(
  1244.     HDC hdc,
  1245.     IMediaSample *pMediaSample,
  1246.     LPRECT lprcSrc,
  1247.     LPRECT lprcDst
  1248.     )
  1249. {
  1250.     ASSERT(m_pMediaType);
  1251.     BITMAPINFOHEADER *pbmi = HEADER(m_pMediaType->Format());
  1252.     BYTE *pImage;
  1253.  
  1254.     // Get the image data buffer
  1255.  
  1256.     HRESULT hr = pMediaSample->GetPointer(&pImage);
  1257.     if (FAILED(hr)) {
  1258.         return FALSE;
  1259.     }
  1260.  
  1261.     RECT SourceRect;
  1262.     RECT TargetRect;
  1263.  
  1264.     if (lprcSrc)
  1265.         SourceRect = *lprcSrc;
  1266.     else  
  1267.         SourceRect = ScaleSourceRect(&m_SourceRect);
  1268.  
  1269.     if (lprcDst)
  1270.         TargetRect = *lprcDst;
  1271.     else  
  1272.         TargetRect = m_TargetRect;
  1273.  
  1274.     LONG lAdjustedSourceTop = SourceRect.top;
  1275.     // if the origin of bitmap is bottom-left, adjust soruce_rect_top
  1276.     // to be the bottom-left corner instead of the top-left.
  1277.     if (pbmi->biHeight > 0) {
  1278.        lAdjustedSourceTop = pbmi->biHeight - SourceRect.bottom;
  1279.     }
  1280.  
  1281.  
  1282.     // Stretch the image when copying to the DC
  1283.  
  1284.     BOOL bRet = (0 != StretchDIBits(hdc,
  1285.                                     TargetRect.left,
  1286.                                     TargetRect.top,
  1287.                                     TargetRect.right - TargetRect.left,
  1288.                                     TargetRect.bottom - TargetRect.top,
  1289.                                     SourceRect.left,
  1290.                                     lAdjustedSourceTop,
  1291.                                     SourceRect.right - SourceRect.left,
  1292.                                     SourceRect.bottom - SourceRect.top,
  1293.                                     pImage,
  1294.                                     (BITMAPINFO *)pbmi,
  1295.                                     DIB_RGB_COLORS,
  1296.                                     SRCCOPY));
  1297.     return bRet;
  1298. }
  1299.  
  1300.  
  1301. // This is called by the owning window object after it has created the window
  1302. // and it's drawing contexts. We are constructed with the base window we'll
  1303. // be drawing into so when given the notification we retrive the device HDCs
  1304. // to draw with. We cannot call these in our constructor as they are virtual
  1305.  
  1306. void CDrawImage::SetDrawContext()
  1307. {
  1308.     m_MemoryDC = m_pBaseWindow->GetMemoryHDC();
  1309.     m_hdc = m_pBaseWindow->GetWindowHDC();
  1310. }
  1311.  
  1312.  
  1313. // This is called to set the target rectangle in the video window, it will be
  1314. // called whenever a WM_SIZE message is retrieved from the message queue. We
  1315. // simply store the rectangle and use it later when we do the drawing calls
  1316.  
  1317. void CDrawImage::SetTargetRect(RECT *pTargetRect)
  1318. {
  1319.     ASSERT(pTargetRect);
  1320.     m_TargetRect = *pTargetRect;
  1321.     SetStretchMode();
  1322. }
  1323.  
  1324.  
  1325. // Return the current target rectangle
  1326.  
  1327. void CDrawImage::GetTargetRect(RECT *pTargetRect)
  1328. {
  1329.     ASSERT(pTargetRect);
  1330.     *pTargetRect = m_TargetRect;
  1331. }
  1332.  
  1333.  
  1334. // This is called when we want to change the section of the image to draw. We
  1335. // use this information in the drawing operation calls later on. We must also
  1336. // see if the source and destination rectangles have the same dimensions. If
  1337. // not we must stretch during the drawing rather than a direct pixel copy
  1338.  
  1339. void CDrawImage::SetSourceRect(RECT *pSourceRect)
  1340. {
  1341.     ASSERT(pSourceRect);
  1342.     m_SourceRect = *pSourceRect;
  1343.     SetStretchMode();
  1344. }
  1345.  
  1346.  
  1347. // Return the current source rectangle
  1348.  
  1349. void CDrawImage::GetSourceRect(RECT *pSourceRect)
  1350. {
  1351.     ASSERT(pSourceRect);
  1352.     *pSourceRect = m_SourceRect;
  1353. }
  1354.  
  1355.  
  1356. // This is called when either the source or destination rectanges change so we
  1357. // can update the stretch flag. If the rectangles don't match we stretch the
  1358. // video during the drawing otherwise we call the fast pixel copy functions
  1359. // NOTE the source and/or the destination rectangle may be completely empty
  1360.  
  1361. void CDrawImage::SetStretchMode()
  1362. {
  1363.     // Calculate the overall rectangle dimensions
  1364.  
  1365.     LONG SourceWidth = m_SourceRect.right - m_SourceRect.left;
  1366.     LONG SinkWidth = m_TargetRect.right - m_TargetRect.left;
  1367.     LONG SourceHeight = m_SourceRect.bottom - m_SourceRect.top;
  1368.     LONG SinkHeight = m_TargetRect.bottom - m_TargetRect.top;
  1369.  
  1370.     m_bStretch = TRUE;
  1371.     if (SourceWidth == SinkWidth) {
  1372.         if (SourceHeight == SinkHeight) {
  1373.             m_bStretch = FALSE;
  1374.         }
  1375.     }
  1376. }
  1377.  
  1378.  
  1379. // Tell us whose allocator we are using. This should be called with TRUE if
  1380. // the filter agrees to use an allocator based around the CImageAllocator
  1381. // SDK base class - whose image buffers are made through CreateDIBSection.
  1382. // Otherwise this should be called with FALSE and we will draw the images
  1383. // using SetDIBitsToDevice and StretchDIBitsToDevice. None of these calls
  1384. // can handle buffers which have non zero strides (like DirectDraw uses)
  1385.  
  1386. void CDrawImage::NotifyAllocator(BOOL bUsingImageAllocator)
  1387. {
  1388.     m_bUsingImageAllocator = bUsingImageAllocator;
  1389. }
  1390.  
  1391.  
  1392. // Are we using the image DIBSECTION allocator
  1393.  
  1394. BOOL CDrawImage::UsingImageAllocator()
  1395. {
  1396.     return m_bUsingImageAllocator;
  1397. }
  1398.  
  1399.  
  1400. // We need the media type of the connection so that we can get the BITMAPINFO
  1401. // from it. We use that in the calls to draw the image such as StretchDIBits
  1402. // and also when updating the colour table held in shared memory DIBSECTIONs
  1403.  
  1404. void CDrawImage::NotifyMediaType(CMediaType *pMediaType)
  1405. {
  1406.     m_pMediaType = pMediaType;
  1407. }
  1408.  
  1409.  
  1410. // We store in this object a cookie maintaining the current palette version.
  1411. // Each time a palettised format is changed we increment this value so that
  1412. // when we come to draw the images we look at the colour table value they
  1413. // have and if less than the current we know to update it. This version is
  1414. // only needed and indeed used when working with shared memory DIBSECTIONs
  1415.  
  1416. LONG CDrawImage::GetPaletteVersion()
  1417. {
  1418.     return m_PaletteVersion;
  1419. }
  1420.  
  1421.  
  1422. // Resets the current palette version number
  1423.  
  1424. void CDrawImage::ResetPaletteVersion()
  1425. {
  1426.     m_PaletteVersion = PALETTE_VERSION;
  1427. }
  1428.  
  1429.  
  1430. // Increment the current palette version
  1431.  
  1432. void CDrawImage::IncrementPaletteVersion()
  1433. {
  1434.     m_PaletteVersion++;
  1435. }
  1436.  
  1437.  
  1438. // Constructor must initialise the base allocator. Each sample we create has a
  1439. // palette version cookie on board. When the source filter changes the palette
  1440. // during streaming the window object increments an internal cookie counter it
  1441. // keeps as well. When it comes to render the samples it looks at the cookie
  1442. // values and if they don't match then it knows to update the sample's colour
  1443. // table. However we always create samples with a cookie of PALETTE_VERSION
  1444. // If there have been multiple format changes and we disconnect and reconnect
  1445. // thereby causing the samples to be reallocated we will create them with a
  1446. // cookie much lower than the current version, this isn't a problem since it
  1447. // will be seen by the window object and the versions will then be updated
  1448.  
  1449. CImageAllocator::CImageAllocator(CBaseFilter *pFilter,
  1450.                                  TCHAR *pName,
  1451.                                  HRESULT *phr) :
  1452.     CBaseAllocator(pName,NULL,phr,TRUE,TRUE),
  1453.     m_pFilter(pFilter)
  1454. {
  1455.     ASSERT(phr);
  1456.     ASSERT(pFilter);
  1457. }
  1458.  
  1459.  
  1460. // Check our DIB buffers have been released
  1461.  
  1462. #ifdef DEBUG
  1463. CImageAllocator::~CImageAllocator()
  1464. {
  1465.     ASSERT(m_bCommitted == FALSE);
  1466. }
  1467. #endif
  1468.  
  1469.  
  1470. // Called from destructor and also from base class to free resources. We work
  1471. // our way through the list of media samples deleting the DIBSECTION created
  1472. // for each. All samples should be back in our list so there is no chance a
  1473. // filter is still using one to write on the display or hold on a pending list
  1474.  
  1475. void CImageAllocator::Free()
  1476. {
  1477.     ASSERT(m_lAllocated == m_lFree.GetCount());
  1478.     EXECUTE_ASSERT(GdiFlush());
  1479.     CImageSample *pSample;
  1480.     DIBDATA *pDibData;
  1481.  
  1482.     while (m_lFree.GetCount() != 0) {
  1483.         pSample = (CImageSample *) m_lFree.RemoveHead();
  1484.         pDibData = pSample->GetDIBData();
  1485.         EXECUTE_ASSERT(DeleteObject(pDibData->hBitmap));
  1486.         EXECUTE_ASSERT(CloseHandle(pDibData->hMapping));
  1487.         delete pSample;
  1488.     }
  1489.  
  1490.     m_lAllocated = 0;
  1491. }
  1492.  
  1493.  
  1494. // Prepare the allocator by checking all the input parameters
  1495.  
  1496. STDMETHODIMP CImageAllocator::CheckSizes(ALLOCATOR_PROPERTIES *pRequest)
  1497. {
  1498.     // Check we have a valid connection
  1499.  
  1500.     if (m_pMediaType == NULL) {
  1501.         return VFW_E_NOT_CONNECTED;
  1502.     }
  1503.  
  1504.     // NOTE We always create a DIB section with the source format type which
  1505.     // may contain a source palette. When we do the BitBlt drawing operation
  1506.     // the target display device may contain a different palette (we may not
  1507.     // have the focus) in which case GDI will do after the palette mapping
  1508.  
  1509.     VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pMediaType->Format();
  1510.  
  1511.     // When we call CreateDIBSection it implicitly maps only enough memory
  1512.     // for the image as defined by thee BITMAPINFOHEADER. If the user asks
  1513.     // for an image smaller than this then we reject the call, if they ask
  1514.     // for an image larger than this then we return what they can have
  1515.  
  1516.     if ((DWORD) pRequest->cbBuffer < pVideoInfo->bmiHeader.biSizeImage) {
  1517.         return E_INVALIDARG;
  1518.     }
  1519.  
  1520.     // Reject buffer prefixes
  1521.  
  1522.     if (pRequest->cbPrefix > 0) {
  1523.         return E_INVALIDARG;
  1524.     }
  1525.  
  1526.     pRequest->cbBuffer = pVideoInfo->bmiHeader.biSizeImage;
  1527.     return NOERROR;
  1528. }
  1529.  
  1530.  
  1531. // Agree the number of media sample buffers and their sizes. The base class
  1532. // this allocator is derived from allows samples to be aligned only on byte
  1533. // boundaries NOTE the buffers are not allocated until the Commit call
  1534.  
  1535. STDMETHODIMP CImageAllocator::SetProperties(
  1536.     ALLOCATOR_PROPERTIES * pRequest,
  1537.     ALLOCATOR_PROPERTIES * pActual)
  1538. {
  1539.     ALLOCATOR_PROPERTIES Adjusted = *pRequest;
  1540.  
  1541.     // Check the parameters fit with the current connection
  1542.  
  1543.     HRESULT hr = CheckSizes(&Adjusted);
  1544.     if (FAILED(hr)) {
  1545.         return hr;
  1546.     }
  1547.     return CBaseAllocator::SetProperties(&Adjusted, pActual);
  1548. }
  1549.  
  1550.  
  1551. // Commit the memory by allocating the agreed number of media samples. For
  1552. // each sample we are committed to creating we have a CImageSample object
  1553. // that we use to manage it's resources. This is initialised with a DIBDATA
  1554. // structure that contains amongst other things the GDI DIBSECTION handle
  1555. // We will access the renderer media type during this so we must have locked
  1556. // (to prevent the format changing for example). The class overrides Commit
  1557. // and Decommit to do this locking (base class Commit in turn calls Alloc)
  1558.  
  1559. HRESULT CImageAllocator::Alloc(void)
  1560. {
  1561.     ASSERT(m_pMediaType);
  1562.     CImageSample *pSample;
  1563.     DIBDATA DibData;
  1564.  
  1565.     // Check the base allocator says it's ok to continue
  1566.  
  1567.     HRESULT hr = CBaseAllocator::Alloc();
  1568.     if (FAILED(hr)) {
  1569.         return hr;
  1570.     }
  1571.  
  1572.     // We create a new memory mapped object although we don't map it into our
  1573.     // address space because GDI does that in CreateDIBSection. It is possible
  1574.     // that we run out of resources before creating all the samples in which
  1575.     // case the available sample list is left with those already created
  1576.  
  1577.     ASSERT(m_lAllocated == 0);
  1578.     while (m_lAllocated < m_lCount) {
  1579.  
  1580.         // Create and initialise a shared memory GDI buffer
  1581.  
  1582.         HRESULT hr = CreateDIB(m_lSize,DibData);
  1583.         if (FAILED(hr)) {
  1584.             return hr;
  1585.         }
  1586.  
  1587.         // Create the sample object and pass it the DIBDATA
  1588.  
  1589.         pSample = CreateImageSample(DibData.pBase,m_lSize);
  1590.         if (pSample == NULL) {
  1591.             EXECUTE_ASSERT(DeleteObject(DibData.hBitmap));
  1592.             EXECUTE_ASSERT(CloseHandle(DibData.hMapping));
  1593.             return E_OUTOFMEMORY;
  1594.         }
  1595.  
  1596.         // Add the completed sample to the available list
  1597.  
  1598.         pSample->SetDIBData(&DibData);
  1599.         m_lFree.Add(pSample);
  1600.         m_lAllocated++;
  1601.     }
  1602.     return NOERROR;
  1603. }
  1604.  
  1605.  
  1606. // We have a virtual method that allocates the samples so that a derived class
  1607. // may override it and allocate more specialised sample objects. So long as it
  1608. // derives its samples from CImageSample then all this code will still work ok
  1609.  
  1610. CImageSample *CImageAllocator::CreateImageSample(LPBYTE pData,LONG Length)
  1611. {
  1612.     HRESULT hr = NOERROR;
  1613.     CImageSample *pSample;
  1614.  
  1615.     // Allocate the new sample and check the return codes
  1616.  
  1617.     pSample = new CImageSample((CBaseAllocator *) this,   // Base class
  1618.                                NAME("Video sample"),      // DEBUG name
  1619.                                (HRESULT *) &hr,           // Return code
  1620.                                (LPBYTE) pData,            // DIB address
  1621.                                (LONG) Length);            // Size of DIB
  1622.  
  1623.     if (pSample == NULL || FAILED(hr)) {
  1624.         delete pSample;
  1625.         return NULL;
  1626.     }
  1627.     return pSample;
  1628. }
  1629.  
  1630.  
  1631. // This function allocates a shared memory block for use by the source filter
  1632. // generating DIBs for us to render. The memory block is created in shared
  1633. // memory so that GDI doesn't have to copy the memory when we do a BitBlt
  1634.  
  1635. HRESULT CImageAllocator::CreateDIB(LONG InSize,DIBDATA &DibData)
  1636. {
  1637.     BITMAPINFO *pbmi;       // Format information for pin
  1638.     BYTE *pBase;            // Pointer to the actual image
  1639.     HANDLE hMapping;        // Handle to mapped object
  1640.     HBITMAP hBitmap;        // DIB section bitmap handle
  1641.  
  1642.     // Create a file mapping object and map into our address space
  1643.  
  1644.     hMapping = CreateFileMapping(hMEMORY,         // Use system page file
  1645.                                  NULL,            // No security attributes
  1646.                                  PAGE_READWRITE,  // Full access to memory
  1647.                                  (DWORD) 0,       // Less than 4Gb in size
  1648.                                  InSize,          // Size of buffer
  1649.                                  NULL);           // No name to section
  1650.     if (hMapping == NULL) {
  1651.         DWORD Error = GetLastError();
  1652.         return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);
  1653.     }
  1654.  
  1655.     // NOTE We always create a DIB section with the source format type which
  1656.     // may contain a source palette. When we do the BitBlt drawing operation
  1657.     // the target display device may contain a different palette (we may not
  1658.     // have the focus) in which case GDI will do after the palette mapping
  1659.  
  1660.     pbmi = (BITMAPINFO *) HEADER(m_pMediaType->Format());
  1661.     if (m_pMediaType == NULL) {
  1662.         DbgBreak("Invalid media type");
  1663.     }
  1664.  
  1665.     hBitmap = CreateDIBSection((HDC) NULL,          // NO device context
  1666.                                pbmi,                // Format information
  1667.                                DIB_RGB_COLORS,      // Use the palette
  1668.                                (VOID **) &pBase,    // Pointer to image data
  1669.                                hMapping,            // Mapped memory handle
  1670.                                (DWORD) 0);          // Offset into memory
  1671.  
  1672.     if (hBitmap == NULL || pBase == NULL) {
  1673.         EXECUTE_ASSERT(CloseHandle(hMapping));
  1674.         DWORD Error = GetLastError();
  1675.         return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, Error);
  1676.     }
  1677.  
  1678.     // Initialise the DIB information structure
  1679.  
  1680.     DibData.hBitmap = hBitmap;
  1681.     DibData.hMapping = hMapping;
  1682.     DibData.pBase = pBase;
  1683.     DibData.PaletteVersion = PALETTE_VERSION;
  1684.     GetObject(hBitmap,sizeof(DIBSECTION),(VOID *)&DibData.DibSection);
  1685.  
  1686.     return NOERROR;
  1687. }
  1688.  
  1689.  
  1690. // We use the media type during the DIBSECTION creation
  1691.  
  1692. void CImageAllocator::NotifyMediaType(CMediaType *pMediaType)
  1693. {
  1694.     m_pMediaType = pMediaType;
  1695. }
  1696.  
  1697.  
  1698. // Overriden to increment the owning object's reference count
  1699.  
  1700. STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingAddRef()
  1701. {
  1702.     return m_pFilter->AddRef();
  1703. }
  1704.  
  1705.  
  1706. // Overriden to decrement the owning object's reference count
  1707.  
  1708. STDMETHODIMP_(ULONG) CImageAllocator::NonDelegatingRelease()
  1709. {
  1710.     return m_pFilter->Release();
  1711. }
  1712.  
  1713.  
  1714. // If you derive a class from CMediaSample that has to transport specialised
  1715. // member variables and entry points then there are three alternate solutions
  1716. // The first is to create a memory buffer larger than actually required by the
  1717. // sample and store your information either at the beginning of it or at the
  1718. // end, the former being moderately safer allowing for misbehaving transform
  1719. // filters. You then adjust the buffer address when you create the base media
  1720. // sample. This has the disadvantage of breaking up the memory allocated to
  1721. // the samples into separate blocks. The second solution is to implement a
  1722. // class derived from CMediaSample and support additional interface(s) that
  1723. // convey your private data. This means defining a custom interface. The final
  1724. // alternative is to create a class that inherits from CMediaSample and adds
  1725. // the private data structures, when you get an IMediaSample in your Receive()
  1726. // call check to see if your allocator is being used, and if it is then cast
  1727. // the IMediaSample into one of your objects. Additional checks can be made
  1728. // to ensure the sample's this pointer is known to be one of your own objects
  1729.  
  1730. CImageSample::CImageSample(CBaseAllocator *pAllocator,
  1731.                            TCHAR *pName,
  1732.                            HRESULT *phr,
  1733.                            LPBYTE pBuffer,
  1734.                            LONG length) :
  1735.     CMediaSample(pName,pAllocator,phr,pBuffer,length),
  1736.     m_bInit(FALSE)
  1737. {
  1738.     ASSERT(pAllocator);
  1739.     ASSERT(pBuffer);
  1740. }
  1741.  
  1742.  
  1743. // Set the shared memory DIB information
  1744.  
  1745. void CImageSample::SetDIBData(DIBDATA *pDibData)
  1746. {
  1747.     ASSERT(pDibData);
  1748.     m_DibData = *pDibData;
  1749.     m_bInit = TRUE;
  1750. }
  1751.  
  1752.  
  1753. // Retrieve the shared memory DIB data
  1754.  
  1755. DIBDATA *CImageSample::GetDIBData()
  1756. {
  1757.     ASSERT(m_bInit == TRUE);
  1758.     return &m_DibData;
  1759. }
  1760.  
  1761.  
  1762. // This class handles the creation of a palette. It is fairly specialist and
  1763. // is intended to simplify palette management for video renderer filters. It
  1764. // is for this reason that the constructor requires three other objects with
  1765. // which it interacts, namely a base media filter, a base window and a base
  1766. // drawing object although the base window or the draw object may be NULL to
  1767. // ignore that part of us. We try not to create and install palettes unless
  1768. // absolutely necessary as they typically require WM_PALETTECHANGED messages
  1769. // to be sent to every window thread in the system which is very expensive
  1770.  
  1771. CImagePalette::CImagePalette(CBaseFilter *pBaseFilter,
  1772.                              CBaseWindow *pBaseWindow,
  1773.                              CDrawImage *pDrawImage) :
  1774.     m_pBaseWindow(pBaseWindow),
  1775.     m_pFilter(pBaseFilter),
  1776.     m_pDrawImage(pDrawImage),
  1777.     m_hPalette(NULL)
  1778. {
  1779.     ASSERT(m_pFilter);
  1780. }
  1781.  
  1782.  
  1783. // Destructor
  1784.  
  1785. #ifdef DEBUG
  1786. CImagePalette::~CImagePalette()
  1787. {
  1788.     ASSERT(m_hPalette == NULL);
  1789. }
  1790. #endif
  1791.  
  1792.  
  1793. // We allow dynamic format changes of the palette but rather than change the
  1794. // palette every time we call this to work out whether an update is required.
  1795. // If the original type didn't use a palette and the new one does (or vica
  1796. // versa) then we return TRUE. If neither formats use a palette we'll return
  1797. // FALSE. If both formats use a palette we compare their colours and return
  1798. // FALSE if they match. This therefore short circuits palette creation unless
  1799. // absolutely necessary since installing palettes is an expensive operation
  1800.  
  1801. BOOL CImagePalette::ShouldUpdate(const VIDEOINFOHEADER *pNewInfo,
  1802.                                  const VIDEOINFOHEADER *pOldInfo)
  1803. {
  1804.     // We may not have a current format yet
  1805.  
  1806.     if (pOldInfo == NULL) {
  1807.         return TRUE;
  1808.     }
  1809.  
  1810.     // Do both formats not require a palette
  1811.  
  1812.     if (ContainsPalette(pNewInfo) == FALSE) {
  1813.         if (ContainsPalette(pOldInfo) == FALSE) {
  1814.             return FALSE;
  1815.         }
  1816.     }
  1817.  
  1818.     // Compare the colours to see if they match
  1819.  
  1820.     DWORD VideoEntries = pNewInfo->bmiHeader.biClrUsed;
  1821.     if (ContainsPalette(pNewInfo) == TRUE)
  1822.         if (ContainsPalette(pOldInfo) == TRUE)
  1823.             if (pOldInfo->bmiHeader.biClrUsed == VideoEntries)
  1824.                 if (pOldInfo->bmiHeader.biClrUsed > 0)
  1825.                     if (memcmp((PVOID) GetBitmapPalette(pNewInfo),
  1826.                                (PVOID) GetBitmapPalette(pOldInfo),
  1827.                                VideoEntries * sizeof(RGBQUAD)) == 0) {
  1828.  
  1829.                         return FALSE;
  1830.                     }
  1831.     return TRUE;
  1832. }
  1833.  
  1834.  
  1835. // This is normally called when the input pin type is set to install a palette
  1836. // We will typically be called from two different places. The first is when we
  1837. // have negotiated a palettised media type after connection, the other is when
  1838. // we receive a new type during processing with an updated palette in which
  1839. // case we must remove and release the resources held by the current palette
  1840.  
  1841. // We can be passed an optional device name if we wish to prepare a palette
  1842. // for a specific monitor on a multi monitor system
  1843.  
  1844. HRESULT CImagePalette::PreparePalette(const CMediaType *pmtNew,
  1845.                                       const CMediaType *pmtOld,
  1846.                       LPSTR szDevice)
  1847. {
  1848.     const VIDEOINFOHEADER *pNewInfo = (VIDEOINFOHEADER *) pmtNew->Format();
  1849.     const VIDEOINFOHEADER *pOldInfo = (VIDEOINFOHEADER *) pmtOld->Format();
  1850.     ASSERT(pNewInfo);
  1851.  
  1852.     // This is an performance optimisation, when we get a media type we check
  1853.     // to see if the format requires a palette change. If either we need one
  1854.     // when previously we didn't or vica versa then this returns TRUE, if we
  1855.     // previously needed a palette and we do now it compares their colours
  1856.  
  1857.     if (ShouldUpdate(pNewInfo,pOldInfo) == FALSE) {
  1858.         NOTE("No update needed");
  1859.         return S_FALSE;
  1860.     }
  1861.  
  1862.     // We must notify the filter graph that the application may have changed
  1863.     // the palette although in practice we don't bother checking to see if it
  1864.     // is really different. If it tries to get the palette either the window
  1865.     // or renderer lock will ensure it doesn't get in until we are finished
  1866.  
  1867.     RemovePalette();
  1868.     m_pFilter->NotifyEvent(EC_PALETTE_CHANGED,0,0);
  1869.  
  1870.     // Do we need a palette for the new format
  1871.  
  1872.     if (ContainsPalette(pNewInfo) == FALSE) {
  1873.         NOTE("New has no palette");
  1874.         return S_FALSE;
  1875.     }
  1876.  
  1877.     if (m_pBaseWindow) {
  1878.         m_pBaseWindow->LockPaletteLock();
  1879.     }
  1880.  
  1881.     // If we're changing the palette on the fly then we increment our palette
  1882.     // cookie which is compared against the cookie also stored in all of our
  1883.     // DIBSECTION media samples. If they don't match when we come to draw it
  1884.     // then we know the sample is out of date and we'll update it's palette
  1885.  
  1886.     NOTE("Making new colour palette");
  1887.     m_hPalette = MakePalette(pNewInfo, szDevice);
  1888.     ASSERT(m_hPalette != NULL);
  1889.  
  1890.     if (m_pBaseWindow) {
  1891.         m_pBaseWindow->UnlockPaletteLock();
  1892.     }
  1893.  
  1894.     // The window in which the new palette is to be realised may be a NULL
  1895.     // pointer to signal that no window is in use, if so we don't call it
  1896.     // Some filters just want to use this object to create/manage palettes
  1897.  
  1898.     if (m_pBaseWindow) m_pBaseWindow->SetPalette(m_hPalette);
  1899.  
  1900.     // This is the only time where we need access to the draw object to say
  1901.     // to it that a new palette will be arriving on a sample real soon. The
  1902.     // constructor may take a NULL pointer in which case we don't call this
  1903.  
  1904.     if (m_pDrawImage) m_pDrawImage->IncrementPaletteVersion();
  1905.     return NOERROR;
  1906. }
  1907.  
  1908.  
  1909. // Helper function to copy a palette out of any kind of VIDEOINFO (ie it may
  1910. // be YUV or true colour) into a palettised VIDEOINFO. We use this changing
  1911. // palettes on DirectDraw samples as a source filter can attach a palette to
  1912. // any buffer (eg YUV) and hand it back. We make a new palette out of that
  1913. // format and then copy the palette colours into the current connection type
  1914.  
  1915. HRESULT CImagePalette::CopyPalette(const CMediaType *pSrc,CMediaType *pDest)
  1916. {
  1917.     // Reset the destination palette before starting
  1918.  
  1919.     VIDEOINFOHEADER *pDestInfo = (VIDEOINFOHEADER *) pDest->Format();
  1920.     pDestInfo->bmiHeader.biClrUsed = 0;
  1921.     pDestInfo->bmiHeader.biClrImportant = 0;
  1922.  
  1923.     // Does the destination have a palette
  1924.  
  1925.     if (PALETTISED(pDestInfo) == FALSE) {
  1926.         NOTE("No destination palette");
  1927.         return S_FALSE;
  1928.     }
  1929.  
  1930.     // Does the source contain a palette
  1931.  
  1932.     const VIDEOINFOHEADER *pSrcInfo = (VIDEOINFOHEADER *) pSrc->Format();
  1933.     if (ContainsPalette(pSrcInfo) == FALSE) {
  1934.         NOTE("No source palette");
  1935.         return S_FALSE;
  1936.     }
  1937.  
  1938.     // The number of colours may be zero filled
  1939.  
  1940.     DWORD PaletteEntries = pSrcInfo->bmiHeader.biClrUsed;
  1941.     if (PaletteEntries == 0) {
  1942.         DWORD Maximum  = (1 << pSrcInfo->bmiHeader.biBitCount);
  1943.         NOTE1("Setting maximum colours (%d)",Maximum);
  1944.         PaletteEntries = Maximum;
  1945.     }
  1946.  
  1947.     // Make sure the destination has enough room for the palette
  1948.  
  1949.     ASSERT(pSrcInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
  1950.     ASSERT(pSrcInfo->bmiHeader.biClrImportant <= PaletteEntries);
  1951.     ASSERT(COLORS(pDestInfo) == GetBitmapPalette(pDestInfo));
  1952.     pDestInfo->bmiHeader.biClrUsed = PaletteEntries;
  1953.     pDestInfo->bmiHeader.biClrImportant = pSrcInfo->bmiHeader.biClrImportant;
  1954.     ULONG BitmapSize = GetBitmapFormatSize(HEADER(pSrcInfo));
  1955.  
  1956.     if (pDest->FormatLength() < BitmapSize) {
  1957.         NOTE("Reallocating destination");
  1958.         pDest->ReallocFormatBuffer(BitmapSize);
  1959.     }
  1960.  
  1961.     // Now copy the palette colours across
  1962.  
  1963.     CopyMemory((PVOID) COLORS(pDestInfo),
  1964.                (PVOID) GetBitmapPalette(pSrcInfo),
  1965.                PaletteEntries * sizeof(RGBQUAD));
  1966.  
  1967.     return NOERROR;
  1968. }
  1969.  
  1970.  
  1971. // This is normally called when the palette is changed (typically during a
  1972. // dynamic format change) to remove any palette we previously installed. We
  1973. // replace it (if necessary) in the video window with a standard VGA palette
  1974. // that should always be available even if this is a true colour display
  1975.  
  1976. HRESULT CImagePalette::RemovePalette()
  1977. {
  1978.     if (m_pBaseWindow) {
  1979.         m_pBaseWindow->LockPaletteLock();
  1980.     }
  1981.  
  1982.     // Do we have a palette to remove
  1983.  
  1984.     if (m_hPalette != NULL) {
  1985.  
  1986.         if (m_pBaseWindow) {
  1987.             // Make sure that the window's palette handle matches
  1988.             // our palette handle.
  1989.             ASSERT(m_hPalette == m_pBaseWindow->GetPalette());
  1990.  
  1991.             m_pBaseWindow->UnsetPalette();
  1992.         }
  1993.  
  1994.         EXECUTE_ASSERT(DeleteObject(m_hPalette));
  1995.         m_hPalette = NULL;
  1996.     }
  1997.  
  1998.     if (m_pBaseWindow) {
  1999.         m_pBaseWindow->UnlockPaletteLock();
  2000.     }
  2001.  
  2002.     return NOERROR;
  2003. }
  2004.  
  2005.  
  2006. // Called to create a palette for the object, the data structure used by GDI
  2007. // to describe a palette is a LOGPALETTE, this includes a variable number of
  2008. // PALETTEENTRY fields which are the colours, we have to convert the RGBQUAD
  2009. // colour fields we are handed in a BITMAPINFO from the media type into these
  2010. // This handles extraction of palettes from true colour and YUV media formats
  2011.  
  2012. // We can be passed an optional device name if we wish to prepare a palette
  2013. // for a specific monitor on a multi monitor system
  2014.  
  2015. HPALETTE CImagePalette::MakePalette(const VIDEOINFOHEADER *pVideoInfo, LPSTR szDevice)
  2016. {
  2017.     ASSERT(ContainsPalette(pVideoInfo) == TRUE);
  2018.     ASSERT(pVideoInfo->bmiHeader.biClrUsed <= iPALETTE_COLORS);
  2019.     BITMAPINFOHEADER *pHeader = HEADER(pVideoInfo);
  2020.  
  2021.     const RGBQUAD *pColours;            // Pointer to the palette
  2022.     LOGPALETTE *lp;                     // Used to create a palette
  2023.     HPALETTE hPalette;                  // Logical palette object
  2024.  
  2025.     lp = (LOGPALETTE *) new BYTE[sizeof(LOGPALETTE) + SIZE_PALETTE];
  2026.     if (lp == NULL) {
  2027.         return NULL;
  2028.     }
  2029.  
  2030.     // Unfortunately for some hare brained reason a GDI palette entry (a
  2031.     // PALETTEENTRY structure) is different to a palette entry from a DIB
  2032.     // format (a RGBQUAD structure) so we have to do the field conversion
  2033.     // The VIDEOINFO containing the palette may be a true colour type so
  2034.     // we use GetBitmapPalette to skip over any bit fields if they exist
  2035.  
  2036.     lp->palVersion = PALVERSION;
  2037.     lp->palNumEntries = (USHORT) pHeader->biClrUsed;
  2038.     if (lp->palNumEntries == 0) 
  2039.         lp->palNumEntries = (WORD) (1 << pHeader->biBitCount);
  2040.     pColours = GetBitmapPalette(pVideoInfo);
  2041.  
  2042.     for (DWORD dwCount = 0;dwCount < lp->palNumEntries;dwCount++) {
  2043.         lp->palPalEntry[dwCount].peRed = pColours[dwCount].rgbRed;
  2044.         lp->palPalEntry[dwCount].peGreen = pColours[dwCount].rgbGreen;
  2045.         lp->palPalEntry[dwCount].peBlue = pColours[dwCount].rgbBlue;
  2046.         lp->palPalEntry[dwCount].peFlags = 0;
  2047.     }
  2048.  
  2049.     MakeIdentityPalette(lp->palPalEntry, lp->palNumEntries, szDevice);
  2050.  
  2051.     // Create a logical palette
  2052.  
  2053.     hPalette = CreatePalette(lp);
  2054.     ASSERT(hPalette != NULL);
  2055.     delete[] lp;
  2056.     return hPalette;
  2057. }
  2058.  
  2059.  
  2060. // GDI does a fair job of compressing the palette entries you give it, so for
  2061. // example if you have five entries with an RGB colour (0,0,0) it will remove
  2062. // all but one of them. When you subsequently draw an image it will map from
  2063. // your logical palette to the compressed device palette. This function looks
  2064. // to see if it is trying to be an identity palette and if so sets the flags
  2065. // field in the PALETTEENTRYs so they remain expanded to boost performance
  2066.  
  2067. // We can be passed an optional device name if we wish to prepare a palette
  2068. // for a specific monitor on a multi monitor system
  2069.  
  2070. HRESULT CImagePalette::MakeIdentityPalette(PALETTEENTRY *pEntry,INT iColours, LPSTR szDevice)
  2071. {
  2072.     PALETTEENTRY SystemEntries[10];         // System palette entries
  2073.     BOOL bIdentityPalette = TRUE;           // Is an identity palette
  2074.     ASSERT(iColours <= iPALETTE_COLORS);    // Should have a palette
  2075.     const int PalLoCount = 10;              // First ten reserved colours
  2076.     const int PalHiStart = 246;             // Last VGA palette entries
  2077.  
  2078.     // Does this have the full colour range
  2079.  
  2080.     if (iColours < 10) {
  2081.         return S_FALSE;
  2082.     }
  2083.  
  2084.     // Apparently some displays have odd numbers of system colours
  2085.  
  2086.     // Get a DC on the right monitor - it's ugly, but this is the way you have
  2087.     // to do it
  2088.     HDC hdc;
  2089.     if (szDevice == NULL || lstrcmpiA(szDevice, "DISPLAY") == 0)
  2090.         hdc = CreateDCA("DISPLAY", NULL, NULL, NULL);
  2091.     else
  2092.         hdc = CreateDCA(NULL, szDevice, NULL, NULL);
  2093.     if (NULL == hdc) {
  2094.         return E_OUTOFMEMORY;
  2095.     }
  2096.     INT Reserved = GetDeviceCaps(hdc,NUMRESERVED);
  2097.     if (Reserved != 20) {
  2098.         DeleteDC(hdc);
  2099.         return S_FALSE;
  2100.     }
  2101.  
  2102.     // Compare our palette against the first ten system entries. The reason I
  2103.     // don't do a memory compare between our two arrays of colours is because
  2104.     // I am not sure what will be in the flags fields for the system entries
  2105.  
  2106.     UINT Result = GetSystemPaletteEntries(hdc,0,PalLoCount,SystemEntries);
  2107.     for (UINT Count = 0;Count < Result;Count++) {
  2108.         if (SystemEntries[Count].peRed != pEntry[Count].peRed ||
  2109.                 SystemEntries[Count].peGreen != pEntry[Count].peGreen ||
  2110.                     SystemEntries[Count].peBlue != pEntry[Count].peBlue) {
  2111.                         bIdentityPalette = FALSE;
  2112.         }
  2113.     }
  2114.  
  2115.     // And likewise compare against the last ten entries
  2116.  
  2117.     Result = GetSystemPaletteEntries(hdc,PalHiStart,PalLoCount,SystemEntries);
  2118.     for (Count = 0;Count < Result;Count++) {
  2119.         if (INT(Count) + PalHiStart < iColours) {
  2120.             if (SystemEntries[Count].peRed != pEntry[PalHiStart + Count].peRed ||
  2121.                     SystemEntries[Count].peGreen != pEntry[PalHiStart + Count].peGreen ||
  2122.                         SystemEntries[Count].peBlue != pEntry[PalHiStart + Count].peBlue) {
  2123.                             bIdentityPalette = FALSE;
  2124.             }
  2125.         }
  2126.     }
  2127.  
  2128.     // If not an identity palette then return S_FALSE
  2129.  
  2130.     DeleteDC(hdc);
  2131.     if (bIdentityPalette == FALSE) {
  2132.         return S_FALSE;
  2133.     }
  2134.  
  2135.     // Set the non VGA entries so that GDI doesn't map them
  2136.  
  2137.     for (Count = PalLoCount;INT(Count) < min(PalHiStart,iColours);Count++) {
  2138.         pEntry[Count].peFlags = PC_NOCOLLAPSE;
  2139.     }
  2140.     return NOERROR;
  2141. }
  2142.  
  2143.  
  2144. // Constructor initialises the VIDEOINFO we keep storing the current display
  2145. // format. The format can be changed at any time, to reset the format held
  2146. // by us call the RefreshDisplayType directly (it's a public method). Since
  2147. // more than one thread will typically call us (ie window threads resetting
  2148. // the type and source threads in the type checking methods) we have a lock
  2149.  
  2150. CImageDisplay::CImageDisplay()
  2151. {
  2152.     RefreshDisplayType(NULL);
  2153. }
  2154.  
  2155.  
  2156.  
  2157. // This initialises the format we hold which contains the display device type
  2158. // We do a conversion on the display device type in here so that when we start
  2159. // type checking input formats we can assume that certain fields have been set
  2160. // correctly, an example is when we make the 16 bit mask fields explicit. This
  2161. // is normally called when we receive WM_DEVMODECHANGED device change messages
  2162.  
  2163. // The optional szDeviceName parameter tells us which monitor we are interested
  2164. // in for a multi monitor system
  2165.  
  2166. HRESULT CImageDisplay::RefreshDisplayType(LPSTR szDeviceName)
  2167. {
  2168.     CAutoLock cDisplayLock(this);
  2169.  
  2170.     // Set the preferred format type
  2171.  
  2172.     ZeroMemory((PVOID)&m_Display,sizeof(VIDEOINFOHEADER)+sizeof(TRUECOLORINFO));
  2173.     m_Display.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2174.     m_Display.bmiHeader.biBitCount = FALSE;
  2175.  
  2176.     // Get the bit depth of a device compatible bitmap
  2177.  
  2178.     // get caps of whichever monitor they are interested in (multi monitor)
  2179.     HDC hdcDisplay;
  2180.     // it's ugly, but this is the way you have to do it
  2181.     if (szDeviceName == NULL || lstrcmpiA(szDeviceName, "DISPLAY") == 0)
  2182.         hdcDisplay = CreateDCA("DISPLAY", NULL, NULL, NULL);
  2183.     else
  2184.         hdcDisplay = CreateDCA(NULL, szDeviceName, NULL, NULL);
  2185.     if (hdcDisplay == NULL) {
  2186.     ASSERT(FALSE);
  2187.     DbgLog((LOG_ERROR,1,TEXT("ACK! Can't get a DC for %hs"),
  2188.                 szDeviceName ? szDeviceName : "<NULL>"));
  2189.     return E_FAIL;
  2190.     } else {
  2191.     DbgLog((LOG_TRACE,3,TEXT("Created a DC for %s"),
  2192.                 szDeviceName ? szDeviceName : "<NULL>"));
  2193.     }
  2194.     HBITMAP hbm = CreateCompatibleBitmap(hdcDisplay,1,1);
  2195.     if ( hbm )
  2196.     {
  2197.         GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
  2198.  
  2199.         // This call will get the colour table or the proper bitfields
  2200.         GetDIBits(hdcDisplay,hbm,0,1,NULL,(BITMAPINFO *)&m_Display.bmiHeader,DIB_RGB_COLORS);
  2201.         DeleteObject(hbm);
  2202.     }
  2203.     DeleteDC(hdcDisplay);
  2204.  
  2205.     // Complete the display type initialisation
  2206.  
  2207.     ASSERT(CheckHeaderValidity(&m_Display));
  2208.     UpdateFormat(&m_Display);
  2209.     DbgLog((LOG_TRACE,3,TEXT("New DISPLAY bit depth =%d"),
  2210.                 m_Display.bmiHeader.biBitCount));
  2211.     return NOERROR;
  2212. }
  2213.  
  2214.  
  2215. // We assume throughout this code that any bitfields masks are allowed no
  2216. // more than eight bits to store a colour component. This checks that the
  2217. // bit count assumption is enforced and also makes sure that all the bits
  2218. // set are contiguous. We return a boolean TRUE if the field checks out ok
  2219.  
  2220. BOOL CImageDisplay::CheckBitFields(const VIDEOINFO *pInput)
  2221. {
  2222.     DWORD *pBitFields = (DWORD *) BITMASKS(pInput);
  2223.  
  2224.     for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
  2225.  
  2226.         // First of all work out how many bits are set
  2227.  
  2228.         DWORD SetBits = CountSetBits(pBitFields[iColour]);
  2229.         if (SetBits > iMAXBITS || SetBits == 0) {
  2230.             NOTE1("Bit fields for component %d invalid",iColour);
  2231.             return FALSE;
  2232.         }
  2233.  
  2234.         // Next work out the number of zero bits prefix
  2235.         DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
  2236.  
  2237.         // This is going to see if all the bits set are contiguous (as they
  2238.         // should be). We know how much to shift them right by from the
  2239.         // count of prefix bits. The number of bits set defines a mask, we
  2240.         // invert this (ones complement) and AND it with the shifted bit
  2241.         // fields. If the result is NON zero then there are bit(s) sticking
  2242.         // out the left hand end which means they are not contiguous
  2243.  
  2244.         DWORD TestField = pBitFields[iColour] >> PrefixBits;
  2245.         DWORD Mask = ULONG_MAX << SetBits;
  2246.         if (TestField & Mask) {
  2247.             NOTE1("Bit fields for component %d not contiguous",iColour);
  2248.             return FALSE;
  2249.         }
  2250.     }
  2251.     return TRUE;
  2252. }
  2253.  
  2254.  
  2255. // This counts the number of bits set in the input field
  2256.  
  2257. DWORD CImageDisplay::CountSetBits(DWORD Field)
  2258. {
  2259.     // This is a relatively well known bit counting algorithm
  2260.  
  2261.     DWORD Count = 0;
  2262.     DWORD init = Field;
  2263.  
  2264.     // Until the input is exhausted, count the number of bits
  2265.  
  2266.     while (init) {
  2267.         init = init & (init - 1);  // Turn off the bottommost bit
  2268.         Count++;
  2269.     }
  2270.     return Count;
  2271. }
  2272.  
  2273.  
  2274. // This counts the number of zero bits upto the first one set NOTE the input
  2275. // field should have been previously checked to ensure there is at least one
  2276. // set although if we don't find one set we return the impossible value 32
  2277.  
  2278. DWORD CImageDisplay::CountPrefixBits(DWORD Field)
  2279. {
  2280.     DWORD Mask = 1;
  2281.     DWORD Count = 0;
  2282.  
  2283.     while (TRUE) {
  2284.         if (Field & Mask) {
  2285.             return Count;
  2286.         }
  2287.         Count++;
  2288.  
  2289.         ASSERT(Mask != 0x80000000);
  2290.         if (Mask == 0x80000000) {
  2291.             return Count;
  2292.         }
  2293.         Mask <<= 1;
  2294.     }
  2295. }
  2296.  
  2297.  
  2298. // This is called to check the BITMAPINFOHEADER for the input type. There are
  2299. // many implicit dependancies between the fields in a header structure which
  2300. // if we validate now make for easier manipulation in subsequent handling. We
  2301. // also check that the BITMAPINFOHEADER matches it's specification such that
  2302. // fields likes the number of planes is one, that it's structure size is set
  2303. // correctly and that the bitmap dimensions have not been set as negative
  2304.  
  2305. BOOL CImageDisplay::CheckHeaderValidity(const VIDEOINFO *pInput)
  2306. {
  2307.     // Check the bitmap width and height are not negative.
  2308.  
  2309.     if (pInput->bmiHeader.biWidth <= 0 ||
  2310.     pInput->bmiHeader.biHeight <= 0) {
  2311.         NOTE("Invalid bitmap dimensions");
  2312.         return FALSE;
  2313.     }
  2314.  
  2315.     // Check the compression is either BI_RGB or BI_BITFIELDS
  2316.  
  2317.     if (pInput->bmiHeader.biCompression != BI_RGB) {
  2318.         if (pInput->bmiHeader.biCompression != BI_BITFIELDS) {
  2319.             NOTE("Invalid compression format");
  2320.             return FALSE;
  2321.         }
  2322.     }
  2323.  
  2324.     // If BI_BITFIELDS compression format check the colour depth
  2325.  
  2326.     if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
  2327.         if (pInput->bmiHeader.biBitCount != 16) {
  2328.             if (pInput->bmiHeader.biBitCount != 32) {
  2329.                 NOTE("BI_BITFIELDS not 16/32 bit depth");
  2330.                 return FALSE;
  2331.             }
  2332.         }
  2333.     }
  2334.  
  2335.     // Check the assumptions about the layout of the bit fields
  2336.  
  2337.     if (pInput->bmiHeader.biCompression == BI_BITFIELDS) {
  2338.         if (CheckBitFields(pInput) == FALSE) {
  2339.             NOTE("Bit fields are not valid");
  2340.             return FALSE;
  2341.         }
  2342.     }
  2343.  
  2344.     // Are the number of planes equal to one
  2345.  
  2346.     if (pInput->bmiHeader.biPlanes != 1) {
  2347.         NOTE("Number of planes not one");
  2348.         return FALSE;
  2349.     }
  2350.  
  2351.     // Check the image size is consistent (it can be zero)
  2352.  
  2353.     if (pInput->bmiHeader.biSizeImage != GetBitmapSize(&pInput->bmiHeader)) {
  2354.         if (pInput->bmiHeader.biSizeImage) {
  2355.             NOTE("Image size incorrectly set");
  2356.             return FALSE;
  2357.         }
  2358.     }
  2359.  
  2360.     // Check the size of the structure
  2361.  
  2362.     if (pInput->bmiHeader.biSize != sizeof(BITMAPINFOHEADER)) {
  2363.         NOTE("Size of BITMAPINFOHEADER wrong");
  2364.         return FALSE;
  2365.     }
  2366.     return CheckPaletteHeader(pInput);
  2367. }
  2368.  
  2369.  
  2370. // This runs a few simple tests against the palette fields in the input to
  2371. // see if it looks vaguely correct. The tests look at the number of palette
  2372. // colours present, the number considered important and the biCompression
  2373. // field which should always be BI_RGB as no other formats are meaningful
  2374.  
  2375. BOOL CImageDisplay::CheckPaletteHeader(const VIDEOINFO *pInput)
  2376. {
  2377.     // The checks here are for palettised videos only
  2378.  
  2379.     if (PALETTISED(pInput) == FALSE) {
  2380.         if (pInput->bmiHeader.biClrUsed) {
  2381.             NOTE("Invalid palette entries");
  2382.             return FALSE;
  2383.         }
  2384.         return TRUE;
  2385.     }
  2386.  
  2387.     // Compression type of BI_BITFIELDS is meaningless for palette video
  2388.  
  2389.     if (pInput->bmiHeader.biCompression != BI_RGB) {
  2390.         NOTE("Palettised video must be BI_RGB");
  2391.         return FALSE;
  2392.     }
  2393.  
  2394.     // Check the number of palette colours is correct
  2395.  
  2396.     if (pInput->bmiHeader.biClrUsed > PALETTE_ENTRIES(pInput)) {
  2397.         NOTE("Too many colours in palette");
  2398.         return FALSE;
  2399.     }
  2400.  
  2401.     // The number of important colours shouldn't exceed the number used
  2402.  
  2403.     if (pInput->bmiHeader.biClrImportant > pInput->bmiHeader.biClrUsed) {
  2404.         NOTE("Too many important colours");
  2405.         return FALSE;
  2406.     }
  2407.     return TRUE;
  2408. }
  2409.  
  2410.  
  2411. // Return the format of the video display
  2412.  
  2413. const VIDEOINFO *CImageDisplay::GetDisplayFormat()
  2414. {
  2415.     return &m_Display;
  2416. }
  2417.  
  2418.  
  2419. // Return TRUE if the display uses a palette
  2420.  
  2421. BOOL CImageDisplay::IsPalettised()
  2422. {
  2423.     return PALETTISED(&m_Display);
  2424. }
  2425.  
  2426.  
  2427. // Return the bit depth of the current display setting
  2428.  
  2429. WORD CImageDisplay::GetDisplayDepth()
  2430. {
  2431.     return m_Display.bmiHeader.biBitCount;
  2432. }
  2433.  
  2434.  
  2435. // Initialise the optional fields in a VIDEOINFO. These are mainly to do with
  2436. // the source and destination rectangles and palette information such as the
  2437. // number of colours present. It simplifies our code just a little if we don't
  2438. // have to keep checking for all the different valid permutations in a header
  2439. // every time we want to do anything with it (an example would be creating a
  2440. // palette). We set the base class media type before calling this function so
  2441. // that the media types between the pins match after a connection is made
  2442.  
  2443. HRESULT CImageDisplay::UpdateFormat(VIDEOINFO *pVideoInfo)
  2444. {
  2445.     ASSERT(pVideoInfo);
  2446.  
  2447.     BITMAPINFOHEADER *pbmi = HEADER(pVideoInfo);
  2448.     SetRectEmpty(&pVideoInfo->rcSource);
  2449.     SetRectEmpty(&pVideoInfo->rcTarget);
  2450.  
  2451.     // Set the number of colours explicitly
  2452.  
  2453.     if (PALETTISED(pVideoInfo)) {
  2454.         if (pVideoInfo->bmiHeader.biClrUsed == 0) {
  2455.             pVideoInfo->bmiHeader.biClrUsed = PALETTE_ENTRIES(pVideoInfo);
  2456.         }
  2457.     }
  2458.  
  2459.     // The number of important colours shouldn't exceed the number used, on
  2460.     // some displays the number of important colours is not initialised when
  2461.     // retrieving the display type so we set the colours used correctly
  2462.  
  2463.     if (pVideoInfo->bmiHeader.biClrImportant > pVideoInfo->bmiHeader.biClrUsed) {
  2464.         pVideoInfo->bmiHeader.biClrImportant = PALETTE_ENTRIES(pVideoInfo);
  2465.     }
  2466.  
  2467.     // Change the image size field to be explicit
  2468.  
  2469.     if (pVideoInfo->bmiHeader.biSizeImage == 0) {
  2470.         pVideoInfo->bmiHeader.biSizeImage = GetBitmapSize(&pVideoInfo->bmiHeader);
  2471.     }
  2472.     return NOERROR;
  2473. }
  2474.  
  2475.  
  2476. // Lots of video rendering filters want code to check proposed formats are ok
  2477. // This checks the VIDEOINFO we are passed as a media type. If the media type
  2478. // is a valid media type then we return NOERROR otherwise E_INVALIDARG. Note
  2479. // however we only accept formats that can be easily displayed in the display
  2480. // so if we are on a 16 bit device we will not accept 24 bit images. The one
  2481. // complexity is that most displays draw 8 bit palettised images efficiently
  2482. // Also if the input format is less colour bits per pixel then we also accept
  2483.  
  2484. HRESULT CImageDisplay::CheckVideoType(const VIDEOINFO *pInput)
  2485. {
  2486.     // First of all check the VIDEOINFOHEADER looks correct
  2487.  
  2488.     if (CheckHeaderValidity(pInput) == FALSE) {
  2489.         return E_INVALIDARG;
  2490.     }
  2491.  
  2492.     // Virtually all devices support palettised images efficiently
  2493.  
  2494.     if (m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount) {
  2495.         if (PALETTISED(pInput) == TRUE) {
  2496.             ASSERT(PALETTISED(&m_Display) == TRUE);
  2497.             NOTE("(Video) Type connection ACCEPTED");
  2498.             return NOERROR;
  2499.         }
  2500.     }
  2501.  
  2502.  
  2503.     // Is the display depth greater than the input format
  2504.  
  2505.     if (m_Display.bmiHeader.biBitCount > pInput->bmiHeader.biBitCount) {
  2506.         NOTE("(Video) Mismatch agreed");
  2507.         return NOERROR;
  2508.     }
  2509.  
  2510.     // Is the display depth less than the input format
  2511.  
  2512.     if (m_Display.bmiHeader.biBitCount < pInput->bmiHeader.biBitCount) {
  2513.         NOTE("(Video) Format mismatch");
  2514.         return E_INVALIDARG;
  2515.     }
  2516.  
  2517.  
  2518.     // Both input and display formats are either BI_RGB or BI_BITFIELDS
  2519.  
  2520.     ASSERT(m_Display.bmiHeader.biBitCount == pInput->bmiHeader.biBitCount);
  2521.     ASSERT(PALETTISED(pInput) == FALSE);
  2522.     ASSERT(PALETTISED(&m_Display) == FALSE);
  2523.  
  2524.     // BI_RGB 16 bit representation is implicitly RGB555, and likewise BI_RGB
  2525.     // 24 bit representation is RGB888. So we initialise a pointer to the bit
  2526.     // fields they really mean and check against the display device format
  2527.     // This is only going to be called when both formats are equal bits pixel
  2528.  
  2529.     const DWORD *pInputMask = GetBitMasks(pInput);
  2530.     const DWORD *pDisplayMask = GetBitMasks((VIDEOINFO *)&m_Display);
  2531.  
  2532.     if (pInputMask[iRED] != pDisplayMask[iRED] ||
  2533.             pInputMask[iGREEN] != pDisplayMask[iGREEN] ||
  2534.                 pInputMask[iBLUE] != pDisplayMask[iBLUE]) {
  2535.  
  2536.         NOTE("(Video) Bit field mismatch");
  2537.         return E_INVALIDARG;
  2538.     }
  2539.  
  2540.     NOTE("(Video) Type connection ACCEPTED");
  2541.     return NOERROR;
  2542. }
  2543.  
  2544.  
  2545. // Return the bit masks for the true colour VIDEOINFO provided
  2546.  
  2547. const DWORD *CImageDisplay::GetBitMasks(const VIDEOINFO *pVideoInfo)
  2548. {
  2549.     static const DWORD FailMasks[] = {0,0,0};
  2550.  
  2551.     if (pVideoInfo->bmiHeader.biCompression == BI_BITFIELDS) {
  2552.         return BITMASKS(pVideoInfo);
  2553.     }
  2554.  
  2555.     ASSERT(pVideoInfo->bmiHeader.biCompression == BI_RGB);
  2556.  
  2557.     switch (pVideoInfo->bmiHeader.biBitCount) {
  2558.         case 16: return bits555;
  2559.         case 24: return bits888;
  2560.         case 32: return bits888;
  2561.         default: return FailMasks;
  2562.     }
  2563. }
  2564.  
  2565.  
  2566. // Check to see if we can support media type pmtIn as proposed by the output
  2567. // pin - We first check that the major media type is video and also identify
  2568. // the media sub type. Then we thoroughly check the VIDEOINFO type provided
  2569. // As well as the contained VIDEOINFO being correct the major type must be
  2570. // video, the subtype a recognised video format and the type GUID correct
  2571.  
  2572. HRESULT CImageDisplay::CheckMediaType(const CMediaType *pmtIn)
  2573. {
  2574.     // Does this have a VIDEOINFOHEADER format block
  2575.  
  2576.     const GUID *pFormatType = pmtIn->FormatType();
  2577.     if (*pFormatType != FORMAT_VideoInfo) {
  2578.         NOTE("Format GUID not a VIDEOINFOHEADER");
  2579.         return E_INVALIDARG;
  2580.     }
  2581.     ASSERT(pmtIn->Format());
  2582.  
  2583.     // Check the format looks reasonably ok
  2584.  
  2585.     ULONG Length = pmtIn->FormatLength();
  2586.     if (Length < SIZE_VIDEOHEADER) {
  2587.         NOTE("Format smaller than a VIDEOHEADER");
  2588.         return E_FAIL;
  2589.     }
  2590.  
  2591.     VIDEOINFO *pInput = (VIDEOINFO *) pmtIn->Format();
  2592.  
  2593.     // Check the major type is MEDIATYPE_Video
  2594.  
  2595.     const GUID *pMajorType = pmtIn->Type();
  2596.     if (*pMajorType != MEDIATYPE_Video) {
  2597.         NOTE("Major type not MEDIATYPE_Video");
  2598.         return E_INVALIDARG;
  2599.     }
  2600.  
  2601.     // Check we can identify the media subtype
  2602.  
  2603.     const GUID *pSubType = pmtIn->Subtype();
  2604.     if (GetBitCount(pSubType) == USHRT_MAX) {
  2605.         NOTE("Invalid video media subtype");
  2606.         return E_INVALIDARG;
  2607.     }
  2608.     return CheckVideoType(pInput);
  2609. }
  2610.  
  2611.  
  2612. // Given a video format described by a VIDEOINFO structure we return the mask
  2613. // that is used to obtain the range of acceptable colours for this type, for
  2614. // example, the mask for a 24 bit true colour format is 0xFF in all cases. A
  2615. // 16 bit 5:6:5 display format uses 0xF8, 0xFC and 0xF8, therefore given any
  2616. // RGB triplets we can AND them with these fields to find one that is valid
  2617.  
  2618. BOOL CImageDisplay::GetColourMask(DWORD *pMaskRed,
  2619.                                   DWORD *pMaskGreen,
  2620.                                   DWORD *pMaskBlue)
  2621. {
  2622.     CAutoLock cDisplayLock(this);
  2623.     *pMaskRed = 0xFF;
  2624.     *pMaskGreen = 0xFF;
  2625.     *pMaskBlue = 0xFF;
  2626.  
  2627.     // If this format is palettised then it doesn't have bit fields
  2628.  
  2629.     if (m_Display.bmiHeader.biBitCount < 16) {
  2630.         return FALSE;
  2631.     }
  2632.  
  2633.     // If this is a 24 bit true colour display then it can handle all the
  2634.     // possible colour component ranges described by a byte. It is never
  2635.     // allowed for a 24 bit colour depth image to have BI_BITFIELDS set
  2636.  
  2637.     if (m_Display.bmiHeader.biBitCount == 24) {
  2638.         ASSERT(m_Display.bmiHeader.biCompression == BI_RGB);
  2639.         return TRUE;
  2640.     }
  2641.  
  2642.     // Calculate the mask based on the format's bit fields
  2643.  
  2644.     const DWORD *pBitFields = (DWORD *) GetBitMasks((VIDEOINFO *)&m_Display);
  2645.     DWORD *pOutputMask[] = { pMaskRed, pMaskGreen, pMaskBlue };
  2646.  
  2647.     // We know from earlier testing that there are no more than iMAXBITS
  2648.     // bits set in the mask and that they are all contiguous. All that
  2649.     // therefore remains is to shift them into the correct position
  2650.  
  2651.     for (INT iColour = iRED;iColour <= iBLUE;iColour++) {
  2652.  
  2653.         // This works out how many bits there are and where they live
  2654.  
  2655.         DWORD PrefixBits = CountPrefixBits(pBitFields[iColour]);
  2656.         DWORD SetBits = CountSetBits(pBitFields[iColour]);
  2657.  
  2658.         // The first shift moves the bit field so that it is right justified
  2659.         // in the DWORD, after which we then shift it back left which then
  2660.         // puts the leading bit in the bytes most significant bit position
  2661.  
  2662.         *(pOutputMask[iColour]) = pBitFields[iColour] >> PrefixBits;
  2663.         *(pOutputMask[iColour]) <<= (iMAXBITS - SetBits);
  2664.     }
  2665.     return TRUE;
  2666. }
  2667.  
  2668.  
  2669. /*  Helper to convert to VIDEOINFOHEADER2
  2670. */
  2671. STDAPI ConvertVideoInfoToVideoInfo2(AM_MEDIA_TYPE *pmt)
  2672. {
  2673.     ASSERT(pmt->formattype == FORMAT_VideoInfo);
  2674.     VIDEOINFO *pVideoInfo = (VIDEOINFO *)pmt->pbFormat;
  2675.     PVOID pvNew = CoTaskMemAlloc(pmt->cbFormat + sizeof(VIDEOINFOHEADER2) -
  2676.                                  sizeof(VIDEOINFOHEADER));
  2677.     if (pvNew == NULL) {
  2678.         return E_OUTOFMEMORY;
  2679.     }
  2680.     CopyMemory(pvNew, pmt->pbFormat, FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));
  2681.     ZeroMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),
  2682.                sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER));
  2683.     CopyMemory((PBYTE)pvNew + FIELD_OFFSET(VIDEOINFOHEADER2, bmiHeader),
  2684.                pmt->pbFormat + FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader),
  2685.                pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader));
  2686.     VIDEOINFOHEADER2 *pVideoInfo2 = (VIDEOINFOHEADER2 *)pvNew;
  2687.     pVideoInfo2->dwPictAspectRatioX = (DWORD)pVideoInfo2->bmiHeader.biWidth;
  2688.     pVideoInfo2->dwPictAspectRatioY = (DWORD)pVideoInfo2->bmiHeader.biHeight;
  2689.     pmt->formattype = FORMAT_VideoInfo2;
  2690.     CoTaskMemFree(pmt->pbFormat);
  2691.     pmt->pbFormat = (PBYTE)pvNew;
  2692.     pmt->cbFormat += sizeof(VIDEOINFOHEADER2) - sizeof(VIDEOINFOHEADER);
  2693.     return S_OK;
  2694. }
  2695.